当前位置: 首页 > news >正文

RenderStage::drawInner

文章目录

  • RenderStage::drawInner
  • OSG渲染后台关系图
  • OSG的渲染流程
  • RenderBin::draw(renderInfo,previous)
  • RenderBin::drawImplementation
    • RenderLeaf::render
      • osg::State::apply(const StateSet*)
      • Drawable::draw(RenderInfo& renderInfo)
        • Drawable::drawInner(RenderInfo& renderInfo)
          • Drawable::drawImplementation

RenderStage::drawInner

void RenderStage::drawInner(osg::RenderInfo& renderInfo,RenderLeaf*& previous, bool& doCopyTexture)
{struct SubFunc{static void applyReadFBO(bool& apply_read_fbo,const FrameBufferObject* read_fbo, osg::State& state){if (read_fbo->isMultisample()){OSG_WARN << "Attempting to read from a"" multisampled framebuffer object. Set a resolve"" framebuffer on the RenderStage to fix this." << std::endl;}if (apply_read_fbo){// Bind the monosampled FBO to read from// ogl操作://创建一个新的fbo//遍历设置的_attachments,编译纹理//ext->glBindFramebuffer(READ_FRAMEBUFFER, fboID);//调用glDrawBuffers(GLsizei n, const GLenum *bufs);,指定在一个渲染操作中输出到多个颜色缓冲区的目标//遍历设置的_attachments,添加附件read_fbo->apply(state, FrameBufferObject::READ_FRAMEBUFFER);apply_read_fbo = false;}}};osg::State& state = *renderInfo.getState();osg::GLExtensions* ext = _fbo.valid() ? state.get<osg::GLExtensions>() : 0;// 判断显示卡是否支持FBObool fbo_supported = ext && ext->isFrameBufferObjectSupported;// 详见RenderStage::runCameraSetUpbool using_multiple_render_targets = fbo_supported && _fbo->hasMultipleRenderingTargets();if (!using_multiple_render_targets){#if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GLES3_AVAILABLE)if( getDrawBufferApplyMask() )state.glDrawBuffer(_drawBuffer);if( getReadBufferApplyMask() )state.glReadBuffer(_readBuffer);#endif}if (fbo_supported){	// ogl操作://创建一个新的fbo//遍历设置的_attachments,编译纹理//ext->glBindFramebuffer(READ_FRAMEBUFFER, fboID);//调用glDrawBuffers(GLsizei n, const GLenum *bufs);,指定在一个渲染操作中输出到多个颜色缓冲区的目标//遍历设置的_attachments,添加附件_fbo->apply(state);}// do the drawing itself.RenderBin::draw(renderInfo,previous);if(state.getCheckForGLErrors()!=osg::State::NEVER_CHECK_GL_ERRORS){if (state.checkGLErrors("after RenderBin::draw(..)")){if ( ext ){GLenum fbstatus = ext->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);if ( fbstatus != GL_FRAMEBUFFER_COMPLETE_EXT ){OSG_NOTICE<<"RenderStage::drawInner(,) FBO status = 0x"<<std::hex<<fbstatus<<std::dec<<std::endl;}}}}// 以下是copy纹理,写image,生成mipmap等操作const FrameBufferObject* read_fbo = fbo_supported ? _fbo.get() : 0;bool apply_read_fbo = false;//glBlitFramebuffer用于在帧缓冲对象之间快速复制像素数据if (fbo_supported && _resolveFbo.valid() && ext->glBlitFramebuffer){GLbitfield blitMask = 0;bool needToBlitColorBuffers = false;//find which buffer types should be copiedfor (FrameBufferObject::AttachmentMap::const_iteratorit = _resolveFbo->getAttachmentMap().begin(),end =_resolveFbo->getAttachmentMap().end(); it != end; ++it){switch (it->first){case Camera::DEPTH_BUFFER:blitMask |= GL_DEPTH_BUFFER_BIT;break;case Camera::STENCIL_BUFFER:blitMask |= GL_STENCIL_BUFFER_BIT;break;case Camera::PACKED_DEPTH_STENCIL_BUFFER:blitMask |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;break;case Camera::COLOR_BUFFER:blitMask |= GL_COLOR_BUFFER_BIT;break;default:needToBlitColorBuffers = true;break;}}// Bind the resolve framebuffer to blit into._fbo->apply(state, FrameBufferObject::READ_FRAMEBUFFER);_resolveFbo->apply(state, FrameBufferObject::DRAW_FRAMEBUFFER);// 如果不使用MSFBO则为0if (blitMask){// Blit to the resolve framebuffer.// Note that (with nvidia 175.16 windows drivers at least) if the read// framebuffer is multisampled then the dimension arguments are ignored// and the whole framebuffer is always copied.// 将READ_FRAMEBUFFER拷贝到DRAW_FRAMEBUFFERext->glBlitFramebuffer(static_cast<GLint>(_viewport->x()), static_cast<GLint>(_viewport->y()),static_cast<GLint>(_viewport->x() + _viewport->width()), static_cast<GLint>(_viewport->y() + _viewport->height()),static_cast<GLint>(_viewport->x()), static_cast<GLint>(_viewport->y()),static_cast<GLint>(_viewport->x() + _viewport->width()), static_cast<GLint>(_viewport->y() + _viewport->height()),blitMask, GL_NEAREST);}#if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GLES3_AVAILABLE)// 拷贝多传输颜色附件if (needToBlitColorBuffers){for (FrameBufferObject::AttachmentMap::const_iteratorit = _resolveFbo->getAttachmentMap().begin(),end =_resolveFbo->getAttachmentMap().end(); it != end; ++it){osg::Camera::BufferComponent attachment = it->first;if (attachment >=osg::Camera::COLOR_BUFFER0){state.glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + (attachment - osg::Camera::COLOR_BUFFER0));state.glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT + (attachment - osg::Camera::COLOR_BUFFER0));ext->glBlitFramebuffer(static_cast<GLint>(_viewport->x()), static_cast<GLint>(_viewport->y()),static_cast<GLint>(_viewport->x() + _viewport->width()), static_cast<GLint>(_viewport->y() + _viewport->height()),static_cast<GLint>(_viewport->x()), static_cast<GLint>(_viewport->y()),static_cast<GLint>(_viewport->x() + _viewport->width()), static_cast<GLint>(_viewport->y() + _viewport->height()),GL_COLOR_BUFFER_BIT, GL_NEAREST);}}// reset the read and draw buffers?  will comment out for now with the assumption that// the buffers will be set explicitly when needed elsewhere.// glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);// glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);}
#endifapply_read_fbo = true;read_fbo = _resolveFbo.get();using_multiple_render_targets = read_fbo->hasMultipleRenderingTargets();}// now copy the rendered image to attached texture.if (doCopyTexture){if (read_fbo) SubFunc::applyReadFBO(apply_read_fbo, read_fbo, state);copyTexture(renderInfo);}//如果有设置附件的image,则将附件读入image中for(std::map< osg::Camera::BufferComponent, Attachment>::const_iterator itr = _bufferAttachmentMap.begin();itr != _bufferAttachmentMap.end();++itr){if (itr->second._image.valid()){if (read_fbo) SubFunc::applyReadFBO(apply_read_fbo, read_fbo, state);#if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE)if (using_multiple_render_targets){int attachment=itr->first;if (attachment==osg::Camera::DEPTH_BUFFER || attachment==osg::Camera::STENCIL_BUFFER) {// assume first buffer rendered to is the one we wantglReadBuffer(read_fbo->getMultipleRenderingTargets()[0]);} else {glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + (attachment - osg::Camera::COLOR_BUFFER0));}} else {if (_readBuffer != GL_NONE){glReadBuffer(_readBuffer);}}#endifGLenum pixelFormat = itr->second._image->getPixelFormat();if (pixelFormat==0) pixelFormat = _imageReadPixelFormat;if (pixelFormat==0) pixelFormat = GL_RGB;GLenum dataType = itr->second._image->getDataType();if (dataType==0) dataType = _imageReadPixelDataType;if (dataType==0) dataType = GL_UNSIGNED_BYTE;itr->second._image->readPixels(static_cast<int>(_viewport->x()),static_cast<int>(_viewport->y()),static_cast<int>(_viewport->width()),static_cast<int>(_viewport->height()),pixelFormat, dataType);}}if (fbo_supported){// 是否需要解除绑定回复默认帧缓冲区if (getDisableFboAfterRender()){// switch off the frame buffer objectGLuint fboId = state.getGraphicsContext() ? state.getGraphicsContext()->getDefaultFboId() : 0;ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId);}doCopyTexture = true;}if (fbo_supported && _camera.valid()){// now generate mipmaps if they are required.const osg::Camera::BufferAttachmentMap& bufferAttachments = _camera->getBufferAttachmentMap();for(osg::Camera::BufferAttachmentMap::const_iterator itr = bufferAttachments.begin();itr != bufferAttachments.end();++itr){if (itr->second._texture.valid() && itr->second._mipMapGeneration){state.setActiveTextureUnit(0);//设置当前纹理单元state.applyTextureAttribute(0, itr->second._texture.get());//用于生成指定纹理的多级渐进纹理(Mipmap):void glGenerateMipmap(GLenum target);//target:GL_TEXTURE_2D,GL_TEXTURE_CUBE_MAP,GL_TEXTURE_3Dext->glGenerateMipmap(itr->second._texture->getTextureTarget());}}}
}

OSG渲染后台关系图

在这里插入图片描述
在这里插入图片描述

  • 如果我们需要自己创建新的派生自Drawable的对象(就像osgText中所实现的),drawable几何体对象的具体实现在于drawImplementation函数。
  • 如果想自己创建一种新的渲染属性(派生自StateAttribute),渲染属性的具现函数为StateAttribute::apply(State&),所有的渲染属性都重写了这一函数,以实现自己的功能。
  • OSG渲染后台与用户层的接口是摄像机类(Camera)。场景中至少有一个主摄像机,它关联了一个图形设备(GraphicsContext,通常是窗口),以及一个渲染器(Renderer);我们可以在场景树中(或者别的视图View中,对于复合视景器而言)添加更多的摄像机,它们可以关联相同的或者其它的图形设备,但都会配有单独的渲染器,用以保存该摄像机的筛选设置、显示器设置等信息。
  • 场景筛选和绘制的工作由渲染器来完成,而图形设备GraphicsContext则负责根据不同时机的选择,调用渲染器的相关函数。例如在单线程模式中,ViewerBase::renderingTraversals函数依次执行Renderer::cull和Renderer::draw函数(后者通过GraphicsContext::runOperations调用),而在多线程模型中调用者的关系将更加错综复杂。
  • OSG渲染后台的调度中心是场景视图(SceneView),它负责保存和执行筛选访问器(CullVisitor)。CullVisitor负责遍历并裁减场景,同时在遍历过程中构建对于场景绘制至关重要的渲染树和状态树;生成的状态树以StateGraph为根节点和各级子节点(其中保存场景树的渲染状态StateSet数据),以RenderLeaf为末端叶节点的内容(其中保存场景树中的几何体Drawable对象);渲染树则以RenderStage为根节点,RenderBin为各级子节点,根据渲染顺序和方法的设定,状态树中的节点和渲染叶(RenderLeaf)被记录到RenderStage和各级RenderBin中;SceneView负责保存和维护状态树和渲染树。
  • 绘制场景时,渲染树中的各级节点将取出保存的渲染叶数据,传递给OSG状态机(State),其中OSG状态机(State)是OpenGL状态机制的封装和实现,也是场景绘制的核心元件。状态机取得渲染叶中的几何数据之后,再向根部遍历状态树,取得该几何体绘制相关的所有渲染状态设置,并亲自或者交由StateAttribute派生类完成渲染状态的实际设定,以及场景元素的实际绘制工作。此时用到的就已经是我们耳熟能详的各种OpenGL函数了。

OSG的渲染流程

1、渲染树的作用是遍历各个渲染元(RenderBin),并按照指定的顺序执行其中各个渲染叶的渲染函数(RenderLeaf::render)。

2、状态树保存了从根节点到当前渲染叶的路径,遍历这条路径并收集所有的渲染属性数据,通过StateGraph::moveStateGraph关闭父节点所有状态,并打开当前节点所有状态,即可获得当前渲染叶渲染所需的所有OpenGL状态数据。

3、渲染叶的渲染函数(RenderLeaf::render)负责向状态机(osg::State)传递渲染状态数据,进而由渲染属性类本身完成参数在OpenGL中的注册和加载工作(StateAttribute::apply);渲染叶还负责调用几何体(Drawable)的绘制函数(drawImplementation),传递顶点和索引数据并完成场景的绘制工作。

RenderBin::draw(renderInfo,previous)

void RenderBin::draw(osg::RenderInfo& renderInfo,RenderLeaf*& previous)
{renderInfo.pushRenderBin(this);if (_drawCallback.valid()){_drawCallback->drawImplementation(this,renderInfo,previous);}else drawImplementation(renderInfo,previous);renderInfo.popRenderBin();
}

RenderBin::drawImplementation

void RenderBin::drawImplementation(osg::RenderInfo& renderInfo,RenderLeaf*& previous)
{osg::State& state = *renderInfo.getState();// OSG_NOTICE<<"begin RenderBin::drawImplementation "<<className()<<" sortMode "<<getSortMode()<<std::endl;unsigned int numToPop = (previous ? StateGraph::numToPop(previous->_parent) : 0);if (numToPop>1) --numToPop;unsigned int insertStateSetPosition = state.getStateSetStackSize() - numToPop;if (_stateset.valid()){state.insertStateSet(insertStateSetPosition, _stateset.get());}// 遍历所有的子渲染元(RenderBin::_bins),其中渲染顺序号小于0的渲染元将在这里执行它们的RenderBin::draw函数,// 由于draw函数内部调用了drawImplementation,因此这构成了一个递归调用,直至渲染树遍历至末端节点。// 在用户程序中,渲染顺序号的设置使用StateSet::setRenderBinDetails函数(RenderBinList的key)。// draw first set of draw bins.RenderBinList::iterator rbitr;for(rbitr = _bins.begin();rbitr!=_bins.end() && rbitr->first<0;++rbitr){rbitr->second->draw(renderInfo,previous);}// 遍历当前RenderBin所保存的所有渲染叶(RenderBin::_renderLeafList),执行RenderLeaf::render函数,实现场景的绘制。// 通常只有被设置为“DepthSortedBin”的渲染元会选择保存渲染叶而非状态节点(StateGraph),因为这样便于按照深度值排序对象。// draw fine grained ordering.for(RenderLeafList::iterator rlitr= _renderLeafList.begin();rlitr!= _renderLeafList.end();++rlitr){RenderLeaf* rl = *rlitr;rl->render(renderInfo,previous);previous = rl;}bool draw_forward = true; //(_sortMode!=SORT_BY_STATE) || (state.getFrameStamp()->getFrameNumber() % 2)==0;// 遍历当前RenderBin所保存的所有状态节点(RenderBin::_stateGraphList),// 获取其中保存的RenderLeaf对象(保存为StateGraph::_leaves),并执行其render函数。// draw coarse grained ordering.if (draw_forward){for(StateGraphList::iterator oitr=_stateGraphList.begin();oitr!=_stateGraphList.end();++oitr){for(StateGraph::LeafList::iterator dw_itr = (*oitr)->_leaves.begin();dw_itr != (*oitr)->_leaves.end();++dw_itr){RenderLeaf* rl = dw_itr->get();rl->render(renderInfo,previous);previous = rl;}}}else{for(StateGraphList::reverse_iterator oitr=_stateGraphList.rbegin();oitr!=_stateGraphList.rend();++oitr){for(StateGraph::LeafList::iterator dw_itr = (*oitr)->_leaves.begin();dw_itr != (*oitr)->_leaves.end();++dw_itr){RenderLeaf* rl = dw_itr->get();rl->render(renderInfo,previous);previous = rl;}}}//遍历所有的子渲染元(RenderBin::_bins),其中渲染顺序号大于0的渲染元此时才执行它们的RenderBin::draw函数。// draw post bins.for(;rbitr!=_bins.end();++rbitr){rbitr->second->draw(renderInfo,previous);}if (_stateset.valid()){state.removeStateSet(insertStateSetPosition);// state.apply();}// OSG_NOTICE<<"end RenderBin::drawImplementation "<<className()<<std::endl;
}
  • 由此可知,渲染树中最先被绘制的将是那些顺序号小于0的末端RenderBin节点,其次则依次是顺序号等于0的末端节点,大于0的末端节点,小于0的倒数第二级节点……而作为渲染树根节点的RenderStage中保存的数据将最后被渲染。
  • 渲染树同一层中不可能存在渲染顺序号相同的渲染元,因为使用setRenderBinDetails设置了相同数字参量的StateSet对象被构建成状态节点(StateGraph)之后,将插入到同一个RenderBin中,而不同name则是不同层级的renderbin。

RenderLeaf::render

  • 渲染叶RenderLeaf是OSG渲染后台中几何体(Drawable)对象的唯一管理者。
  • 而这里的render函数主要负责获取之前保存的Drawable指针,并将它们传递给负责渲染状态处理的State类,以及执行Drawable::draw函数。
void RenderLeaf::render(osg::RenderInfo& renderInfo,RenderLeaf* previous)
{osg::State& state = *renderInfo.getState();// don't draw this leaf if the abort中止 rendering flag has been set.if (state.getAbortRendering()){//cout << "early abort"<<endl;return;}if (previous){// apply matrices if required.state.applyProjectionMatrix(_projection.get());state.applyModelViewMatrix(_modelview.get());// 如果当前渲染叶与上一次处理的渲染叶父节点不同,则需要遍历状态树中相应的路径,// 并更新State状态机中保存的渲染状态数据(采用std::map类型,分别名为_modeMap和_attributeMap)。// apply state if required.StateGraph* prev_rg = previous->_parent;StateGraph* prev_rg_parent = prev_rg->_parent;StateGraph* rg = _parent;if (prev_rg_parent!=rg->_parent){// 它负责清除上一次使用的各种渲染状态,再沿着状态树中的路径,依次添加当前渲染叶所需的数据。// 最后执行函数State::apply(const StateSet*),由OSG状态机处理并执行相应的OpenGL指令。StateGraph::moveStateGraph(state,prev_rg_parent,rg->_parent);// send state changes and matrix changes to OpenGL.state.apply(rg->getStateSet());}else if (rg!=prev_rg){// send state changes and matrix changes to OpenGL.state.apply(rg->getStateSet());}// 设置着色器osg内置变量// if we are using osg::Program which requires OSG's generated uniforms to track// modelview and projection matrices then apply them now.if (state.getUseModelViewAndProjectionUniforms()) state.applyModelViewAndProjectionUniformsIfRequired();// 执行此渲染叶所保存的Drawable对象的draw函数,完成几何体的绘制。// Geometry对象将在这一函数中(实际上是Drawable::drawImplementation)向状态机传递顶点和索引数据,// 并交由状态机对象来完成几何数据的绘制。// draw the drawable_drawable->draw(renderInfo);}else{// apply matrices if required.state.applyProjectionMatrix(_projection.get());state.applyModelViewMatrix(_modelview.get());// apply state if required.StateGraph::moveStateGraph(state,NULL,_parent->_parent);state.apply(_parent->getStateSet());// if we are using osg::Program which requires OSG's generated uniforms to track// modelview and projection matrices then apply them now.if (state.getUseModelViewAndProjectionUniforms()) state.applyModelViewAndProjectionUniformsIfRequired();// draw the drawable_drawable->draw(renderInfo);}if (_dynamic){state.decrementDynamicObjectCount();}// OSG_NOTICE<<"RenderLeaf "<<_drawable->getName()<<" "<<_depth<<std::endl;
}

osg::State::apply(const StateSet*)

Drawable::draw(RenderInfo& renderInfo)

inline void Drawable::draw(RenderInfo& renderInfo) const
{State& state = *renderInfo.getState();bool useVertexArrayObject = state.useVertexArrayObject(_useVertexArrayObject);if (useVertexArrayObject){unsigned int contextID = renderInfo.getContextID();VertexArrayState* vas = _vertexArrayStateList[contextID].get();if (!vas){_vertexArrayStateList[contextID] = vas = createVertexArrayState(renderInfo);}else{// vas->setRequiresSetArrays(getDataVariance()==osg::Object::DYNAMIC);}State::SetCurrentVertexArrayStateProxy setVASProxy(state, vas);// 但凡是使用ogl核心模式,都要使用vaostate.bindVertexArrayObject(vas);drawInner(renderInfo);vas->setRequiresSetArrays(getDataVariance()==osg::Object::DYNAMIC);return;}// TODO, add check against whether VAO is active and supportedif (state.getCurrentVertexArrayState()){//OSG_NOTICE<<"state.getCurrentVertexArrayState()->getVertexArrayObject()="<< state.getCurrentVertexArrayState()->getVertexArrayObject()<<std::endl;state.bindVertexArrayObject(state.getCurrentVertexArrayState());}// 使用显示列表
#ifdef OSG_GL_DISPLAYLISTS_AVAILABLEif (!state.useVertexBufferObject(_supportsVertexBufferObjects && _useVertexBufferObjects) && _useDisplayList){// get the contextID (user defined ID of 0 upwards) for the// current OpenGL context.unsigned int contextID = renderInfo.getContextID();// get the globj for the current contextID.GLuint& globj = _globjList[contextID];if( globj == 0 ){// compile the display listglobj = generateDisplayList(contextID, getGLObjectSizeHint());glNewList( globj, GL_COMPILE );drawInner(renderInfo);glEndList();}// call the display listglCallList( globj);}else
#endif{// if state.previousVertexArrayState() is different than currentVertexArrayState bind current// OSG_NOTICE<<"Fallback drawInner()........................"<<std::endl;drawInner(renderInfo);}
}
#endif
Drawable::drawInner(RenderInfo& renderInfo)
inline void drawInner(RenderInfo& renderInfo) const{if (_drawCallback.valid())_drawCallback->drawImplementation(renderInfo,this);elsedrawImplementation(renderInfo);}
Drawable::drawImplementation
 /** drawImplementation(RenderInfo&) is a pure virtual method for the actual implementation of OpenGL drawing calls, such as vertex arrays and primitives, that* must be implemented in concrete subclasses of the Drawable base class, examples include osg::Geometry and osg::ShapeDrawable.* drawImplementation(RenderInfo&) is called from the draw(RenderInfo&) method, with the draw method handling management of OpenGL display lists,* and drawImplementation(RenderInfo&) handling the actual drawing itself.* renderInfo : The osg::RenderInfo object that encapsulates the current rendering information including the osg::State OpenGL state for the current graphics context. */virtual void drawImplementation(RenderInfo& /*renderInfo*/) const {}

相关文章:

  • 学习笔记:黑马程序员JavaWeb开发教程(2025.3.23)
  • 计算机网络综合实验指南
  • 大模型安全吗?数据泄露与AI伦理的黑暗面!
  • ModuleNotFoundError: No module named ‘vllm.lora.peft_helper‘原因和解决方式
  • C++进阶-多态
  • 【C++ 程序设计】实战:C++ 实践练习题(31~40)
  • sort自定义排序函数tips【C++】
  • 维普期刊(瑞数6)分析
  • Android开发中广播(Broadcast)技术详解
  • rag搭建,是如何进行向量匹配检索的?
  • Transfomer的本质
  • 【Android】Wallpaper学习
  • 博客系统案例练习2-用户注册-redis
  • 数据库知识
  • 电解电容失效分析过程、失效分析报告
  • 450.删除二叉搜索树中的节点
  • 【Vulkan 入门系列】创建交换链、图像视图和渲染通道(四)
  • 【2025面试常问Java八股】AQS介绍(AbstractQueuedSynchronizer 抽象队列同步器)
  • PyCharm使用Anaconda 中的虚拟环境
  • PowerBi如何制作KPI的总览页?
  • “6+2”小复式追加票!松江购彩者擒大乐透1672万头奖
  • 《大家聊中国式现代化》明天全网推出
  • 中共中央办公厅、国务院办公厅印发《农村基层干部廉洁履行职责规定》
  • 网络社群的早期历史及其启示
  • 网信部门持续整治利用未成年人形象不当牟利问题
  • 对话|听老婆的话,UFC“下山虎”张名扬的铁汉柔情