diff options
| author | assiduous <assiduous@diligentgraphics.com> | 2020-08-11 01:45:28 +0000 |
|---|---|---|
| committer | assiduous <assiduous@diligentgraphics.com> | 2020-08-11 01:45:28 +0000 |
| commit | 80d0d972308444d949eb1c363fb81864143c0762 (patch) | |
| tree | d12cb2ad7589607e9a42370a319a276393670ce0 /Graphics/GraphicsEngineOpenGL | |
| parent | D3D12 backend: disabled warnings about render target and depth stencil clear ... (diff) | |
| download | DiligentCore-80d0d972308444d949eb1c363fb81864143c0762.tar.gz DiligentCore-80d0d972308444d949eb1c363fb81864143c0762.zip | |
Initial implementation of render passes in GL backend
Diffstat (limited to 'Graphics/GraphicsEngineOpenGL')
7 files changed, 219 insertions, 108 deletions
diff --git a/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp b/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp index ad9ff03b..11886c11 100644 --- a/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp @@ -27,6 +27,8 @@ #pragma once +#include <vector> + #include "DeviceContextGL.h" #include "DeviceContextBase.hpp" #include "BaseInterfacesGL.h" @@ -264,6 +266,8 @@ private: __forceinline void PrepareForIndirectDraw(IBuffer* pAttribsBuffer); __forceinline void PostDraw(); + void BeginSubpass(); + Uint32 m_CommitedResourcesTentativeBarriers = 0; std::vector<class TextureBaseGL*> m_BoundWritableTextures; @@ -274,6 +278,8 @@ private: bool m_IsDefaultFBOBound = false; GLObjectWrappers::GLFrameBufferObj m_DefaultFBO; + + std::vector<OptimizedClearValue> m_AttachmentClearValues; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/include/FBOCache.hpp b/Graphics/GraphicsEngineOpenGL/include/FBOCache.hpp index 98e097d2..928da3f8 100644 --- a/Graphics/GraphicsEngineOpenGL/include/FBOCache.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/FBOCache.hpp @@ -51,6 +51,11 @@ public: FBOCache& operator = ( FBOCache&&) = delete; // clang-format on + static GLObjectWrappers::GLFrameBufferObj CreateFBO(class GLContextState& ContextState, + Uint32 NumRenderTargets, + TextureViewGLImpl* ppRTVs[], + TextureViewGLImpl* pDSV); + const GLObjectWrappers::GLFrameBufferObj& GetFBO(Uint32 NumRenderTargets, TextureViewGLImpl* ppRTVs[], TextureViewGLImpl* pDSV, @@ -91,7 +96,7 @@ private: // Multimap that sets up correspondence between unique texture id and all // FBOs it is used in - std::unordered_multimap<Diligent::UniqueIdentifier, FBOCacheKey> m_TexIdToKey; + std::unordered_multimap<UniqueIdentifier, FBOCacheKey> m_TexIdToKey; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/include/FramebufferGLImpl.hpp b/Graphics/GraphicsEngineOpenGL/include/FramebufferGLImpl.hpp index 8144ffe3..92d05e46 100644 --- a/Graphics/GraphicsEngineOpenGL/include/FramebufferGLImpl.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/FramebufferGLImpl.hpp @@ -30,9 +30,12 @@ /// \file /// Declaration of Diligent::FramebufferGLImpl class +#include <vector> + #include "RenderDeviceGL.h" #include "FramebufferBase.hpp" #include "RenderDeviceGLImpl.hpp" +#include "GLObjectWrapper.hpp" namespace Diligent { @@ -47,8 +50,17 @@ public: FramebufferGLImpl(IReferenceCounters* pRefCounters, RenderDeviceGLImpl* pDevice, + GLContextState& CtxState, const FramebufferDesc& Desc); ~FramebufferGLImpl(); + + const GLObjectWrappers::GLFrameBufferObj& GetSubpassFramebuffer(Uint32 subpass) + { + return m_SubpassFramebuffers[subpass]; + } + +private: + std::vector<GLObjectWrappers::GLFrameBufferObj> m_SubpassFramebuffers; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp index 478be058..265f9f0f 100644 --- a/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp @@ -416,22 +416,70 @@ void DeviceContextGLImpl::ResetRenderTargets() m_ContextState.InvalidateFBO(); } +void DeviceContextGLImpl::BeginSubpass() +{ + VERIFY_EXPR(m_pActiveRenderPass); + VERIFY_EXPR(m_pBoundFramebuffer); + const auto& RPDesc = m_pActiveRenderPass->GetDesc(); + VERIFY_EXPR(m_SubpassIndex < RPDesc.SubpassCount); + const auto& SubpassDesc = RPDesc.pSubpasses[m_SubpassIndex]; + const auto& FBDesc = m_pBoundFramebuffer->GetDesc(); + for (Uint32 rt = 0; rt < SubpassDesc.RenderTargetAttachmentCount; ++rt) + { + const auto& RTAttachmentRef = SubpassDesc.pRenderTargetAttachments[rt]; + if (RTAttachmentRef.AttachmentIndex != ATTACHMENT_UNUSED) + { + const auto& AttachmentDesc = RPDesc.pAttachments[RTAttachmentRef.AttachmentIndex]; + auto FirstLastUse = m_pActiveRenderPass->GetAttachmentFirstLastUse(RTAttachmentRef.AttachmentIndex); + if (FirstLastUse.first == m_SubpassIndex && AttachmentDesc.LoadOp == ATTACHMENT_LOAD_OP_CLEAR) + { + auto* pRTV = FBDesc.ppAttachments[RTAttachmentRef.AttachmentIndex]; + ClearRenderTarget(pRTV, m_AttachmentClearValues[RTAttachmentRef.AttachmentIndex].Color, RESOURCE_STATE_TRANSITION_MODE_NONE); + } + } + } + + if (SubpassDesc.pDepthStencilAttachment != nullptr && SubpassDesc.pDepthStencilAttachment->AttachmentIndex != ATTACHMENT_UNUSED) + { + auto DepthAttachmentIndex = SubpassDesc.pDepthStencilAttachment->AttachmentIndex; + const auto& AttachmentDesc = RPDesc.pAttachments[DepthAttachmentIndex]; + auto FirstLastUse = m_pActiveRenderPass->GetAttachmentFirstLastUse(DepthAttachmentIndex); + if (FirstLastUse.first == m_SubpassIndex && AttachmentDesc.LoadOp == ATTACHMENT_LOAD_OP_CLEAR) + { + auto* pDSV = FBDesc.ppAttachments[DepthAttachmentIndex]; + const auto& ClearVal = m_AttachmentClearValues[DepthAttachmentIndex].DepthStencil; + ClearDepthStencil(pDSV, CLEAR_DEPTH_FLAG | CLEAR_STENCIL_FLAG, ClearVal.Depth, ClearVal.Stencil, RESOURCE_STATE_TRANSITION_MODE_NONE); + } + } +} + void DeviceContextGLImpl::BeginRenderPass(const BeginRenderPassAttribs& Attribs) { TDeviceContextBase::BeginRenderPass(Attribs); - UNEXPECTED("Method not implemented"); + + m_AttachmentClearValues.resize(Attribs.ClearValueCount); + for (Uint32 i = 0; i < Attribs.ClearValueCount; ++i) + m_AttachmentClearValues[i] = Attribs.pClearValues[i]; + + VERIFY_EXPR(m_pBoundFramebuffer); + m_ContextState.BindFBO(m_pBoundFramebuffer->GetSubpassFramebuffer(m_SubpassIndex)); + SetViewports(1, nullptr, 0, 0); + + BeginSubpass(); } void DeviceContextGLImpl::NextSubpass() { TDeviceContextBase::NextSubpass(); - UNEXPECTED("Method not implemented"); + + m_ContextState.BindFBO(m_pBoundFramebuffer->GetSubpassFramebuffer(m_SubpassIndex)); + + BeginSubpass(); } void DeviceContextGLImpl::EndRenderPass() { TDeviceContextBase::EndRenderPass(); - UNEXPECTED("Method not implemented"); } void DeviceContextGLImpl::BindProgramResources(Uint32& NewMemoryBarriers, IShaderResourceBinding* pResBinding) diff --git a/Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp b/Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp index 5d18a955..e11a3af2 100644 --- a/Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp @@ -109,6 +109,116 @@ void FBOCache::OnReleaseTexture(ITexture* pTexture) m_TexIdToKey.erase(EqualRange.first, EqualRange.second); } +GLObjectWrappers::GLFrameBufferObj FBOCache::CreateFBO(GLContextState& ContextState, + Uint32 NumRenderTargets, + TextureViewGLImpl* ppRTVs[], + TextureViewGLImpl* pDSV) +{ + GLObjectWrappers::GLFrameBufferObj FBO{true}; + + ContextState.BindFBO(FBO); + + // Initialize the FBO + for (Uint32 rt = 0; rt < NumRenderTargets; ++rt) + { + if (auto* pRTView = ppRTVs[rt]) + { + const auto& RTVDesc = pRTView->GetDesc(); + auto* pColorTexGL = pRTView->GetTexture<TextureBaseGL>(); + pColorTexGL->AttachToFramebuffer(RTVDesc, GL_COLOR_ATTACHMENT0 + rt); + } + } + + if (pDSV != nullptr) + { + const auto& DSVDesc = pDSV->GetDesc(); + auto* pDepthTexGL = pDSV->GetTexture<TextureBaseGL>(); + GLenum AttachmentPoint = 0; + if (DSVDesc.Format == TEX_FORMAT_D32_FLOAT || + DSVDesc.Format == TEX_FORMAT_D16_UNORM) + { +#ifdef DILIGENT_DEBUG + { + const auto GLTexFmt = pDepthTexGL->GetGLTexFormat(); + VERIFY(GLTexFmt == GL_DEPTH_COMPONENT32F || GLTexFmt == GL_DEPTH_COMPONENT16, + "Inappropriate internal texture format (", GLTexFmt, + ") for depth attachment. GL_DEPTH_COMPONENT32F or GL_DEPTH_COMPONENT16 is expected"); + } +#endif + AttachmentPoint = GL_DEPTH_ATTACHMENT; + } + else if (DSVDesc.Format == TEX_FORMAT_D32_FLOAT_S8X24_UINT || + DSVDesc.Format == TEX_FORMAT_D24_UNORM_S8_UINT) + { +#ifdef DILIGENT_DEBUG + { + const auto GLTexFmt = pDepthTexGL->GetGLTexFormat(); + VERIFY(GLTexFmt == GL_DEPTH24_STENCIL8 || GLTexFmt == GL_DEPTH32F_STENCIL8, + "Inappropriate internal texture format (", GLTexFmt, + ") for depth-stencil attachment. GL_DEPTH24_STENCIL8 or GL_DEPTH32F_STENCIL8 is expected"); + } +#endif + AttachmentPoint = GL_DEPTH_STENCIL_ATTACHMENT; + } + else + { + UNEXPECTED(GetTextureFormatAttribs(DSVDesc.Format).Name, " is not valid depth-stencil view format"); + } + pDepthTexGL->AttachToFramebuffer(DSVDesc, AttachmentPoint); + } + + // We now need to set mapping between shader outputs and + // color attachments. This largely redundant step is performed + // by glDrawBuffers() + // clang-format off + static const GLenum DrawBuffers[] = + { + GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2, + GL_COLOR_ATTACHMENT3, + GL_COLOR_ATTACHMENT4, + GL_COLOR_ATTACHMENT5, + GL_COLOR_ATTACHMENT6, + GL_COLOR_ATTACHMENT7, + GL_COLOR_ATTACHMENT8, + GL_COLOR_ATTACHMENT9, + GL_COLOR_ATTACHMENT10, + GL_COLOR_ATTACHMENT11, + GL_COLOR_ATTACHMENT12, + GL_COLOR_ATTACHMENT13, + GL_COLOR_ATTACHMENT14, + GL_COLOR_ATTACHMENT15 + }; + // clang-format on + + // The state set by glDrawBuffers() is part of the state of the framebuffer. + // So it can be set up once and left it set. + glDrawBuffers(NumRenderTargets, DrawBuffers); + CHECK_GL_ERROR("Failed to set draw buffers via glDrawBuffers()"); + + GLenum Status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (Status != GL_FRAMEBUFFER_COMPLETE) + { + const Char* StatusString = "Unknown"; + switch (Status) + { + // clang-format off + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: StatusString = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: StatusString = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; break; + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: StatusString = "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"; break; + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: StatusString = "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"; break; + case GL_FRAMEBUFFER_UNSUPPORTED: StatusString = "GL_FRAMEBUFFER_UNSUPPORTED"; break; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: StatusString = "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; break; + case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: StatusString = "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS"; break; + // clang-format on + } + LOG_ERROR("Framebuffer is incomplete. FB status: ", StatusString); + UNEXPECTED("Framebuffer is incomplete"); + } + return FBO; +} + const GLObjectWrappers::GLFrameBufferObj& FBOCache::GetFBO(Uint32 NumRenderTargets, TextureViewGLImpl* ppRTVs[], TextureViewGLImpl* pDSV, @@ -163,108 +273,7 @@ const GLObjectWrappers::GLFrameBufferObj& FBOCache::GetFBO(Uint32 Nu else { // Create a new FBO - GLObjectWrappers::GLFrameBufferObj NewFBO(true); - - ContextState.BindFBO(NewFBO); - - // Initialize the FBO - for (Uint32 rt = 0; rt < NumRenderTargets; ++rt) - { - if (auto* pRTView = ppRTVs[rt]) - { - const auto& RTVDesc = pRTView->GetDesc(); - auto* pColorTexGL = pRTView->GetTexture<TextureBaseGL>(); - pColorTexGL->AttachToFramebuffer(RTVDesc, GL_COLOR_ATTACHMENT0 + rt); - } - } - - if (pDSV != nullptr) - { - const auto& DSVDesc = pDSV->GetDesc(); - auto* pDepthTexGL = pDSV->GetTexture<TextureBaseGL>(); - GLenum AttachmentPoint = 0; - if (DSVDesc.Format == TEX_FORMAT_D32_FLOAT || - DSVDesc.Format == TEX_FORMAT_D16_UNORM) - { -#ifdef DILIGENT_DEBUG - { - const auto GLTexFmt = pDepthTexGL->GetGLTexFormat(); - VERIFY(GLTexFmt == GL_DEPTH_COMPONENT32F || GLTexFmt == GL_DEPTH_COMPONENT16, - "Inappropriate internal texture format (", GLTexFmt, - ") for depth attachment. GL_DEPTH_COMPONENT32F or GL_DEPTH_COMPONENT16 is expected"); - } -#endif - AttachmentPoint = GL_DEPTH_ATTACHMENT; - } - else if (DSVDesc.Format == TEX_FORMAT_D32_FLOAT_S8X24_UINT || - DSVDesc.Format == TEX_FORMAT_D24_UNORM_S8_UINT) - { -#ifdef DILIGENT_DEBUG - { - const auto GLTexFmt = pDepthTexGL->GetGLTexFormat(); - VERIFY(GLTexFmt == GL_DEPTH24_STENCIL8 || GLTexFmt == GL_DEPTH32F_STENCIL8, - "Inappropriate internal texture format (", GLTexFmt, - ") for depth-stencil attachment. GL_DEPTH24_STENCIL8 or GL_DEPTH32F_STENCIL8 is expected"); - } -#endif - AttachmentPoint = GL_DEPTH_STENCIL_ATTACHMENT; - } - else - { - UNEXPECTED(GetTextureFormatAttribs(DSVDesc.Format).Name, " is not valid depth-stencil view format"); - } - pDepthTexGL->AttachToFramebuffer(DSVDesc, AttachmentPoint); - } - - // We now need to set mapping between shader outputs and - // color attachments. This largely redundant step is performed - // by glDrawBuffers() - // clang-format off - static const GLenum DrawBuffers[] = - { - GL_COLOR_ATTACHMENT0, - GL_COLOR_ATTACHMENT1, - GL_COLOR_ATTACHMENT2, - GL_COLOR_ATTACHMENT3, - GL_COLOR_ATTACHMENT4, - GL_COLOR_ATTACHMENT5, - GL_COLOR_ATTACHMENT6, - GL_COLOR_ATTACHMENT7, - GL_COLOR_ATTACHMENT8, - GL_COLOR_ATTACHMENT9, - GL_COLOR_ATTACHMENT10, - GL_COLOR_ATTACHMENT11, - GL_COLOR_ATTACHMENT12, - GL_COLOR_ATTACHMENT13, - GL_COLOR_ATTACHMENT14, - GL_COLOR_ATTACHMENT15 - }; - // clang-format on - - // The state set by glDrawBuffers() is part of the state of the framebuffer. - // So it can be set up once and left it set. - glDrawBuffers(NumRenderTargets, DrawBuffers); - CHECK_GL_ERROR("Failed to set draw buffers via glDrawBuffers()"); - - GLenum Status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (Status != GL_FRAMEBUFFER_COMPLETE) - { - const Char* StatusString = "Unknown"; - switch (Status) - { - // clang-format off - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: StatusString = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; break; - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: StatusString = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; break; - case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: StatusString = "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"; break; - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: StatusString = "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"; break; - case GL_FRAMEBUFFER_UNSUPPORTED: StatusString = "GL_FRAMEBUFFER_UNSUPPORTED"; break; - case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: StatusString = "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; break; - case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: StatusString = "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS"; break; - // clang-format on - } - LOG_ERROR("Framebuffer is incomplete. FB status: ", StatusString); - UNEXPECTED("Framebuffer is incomplete"); - } + auto NewFBO = CreateFBO(ContextState, NumRenderTargets, ppRTVs, pDSV); auto NewElems = m_Cache.emplace(std::make_pair(Key, std::move(NewFBO))); // New element must be actually inserted diff --git a/Graphics/GraphicsEngineOpenGL/src/FramebufferGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/FramebufferGLImpl.cpp index 6ec1d1f1..d9f8341f 100644 --- a/Graphics/GraphicsEngineOpenGL/src/FramebufferGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/FramebufferGLImpl.cpp @@ -28,16 +28,43 @@ #include "pch.h" #include "FramebufferGLImpl.hpp" -#include "EngineMemory.h" +#include "FBOCache.hpp" +#include "TextureViewGLImpl.hpp" namespace Diligent { FramebufferGLImpl::FramebufferGLImpl(IReferenceCounters* pRefCounters, RenderDeviceGLImpl* pDevice, + GLContextState& CtxState, const FramebufferDesc& Desc) : TFramebufferBase{pRefCounters, pDevice, Desc} { + const auto& RPDesc = m_Desc.pRenderPass->GetDesc(); + m_SubpassFramebuffers.reserve(RPDesc.SubpassCount); + for (Uint32 subpass = 0; subpass < RPDesc.SubpassCount; ++subpass) + { + const auto& SubpassDesc = RPDesc.pSubpasses[subpass]; + + TextureViewGLImpl* ppRTVs[MAX_RENDER_TARGETS] = {}; + TextureViewGLImpl* pDSV = nullptr; + + for (Uint32 rt = 0; rt < SubpassDesc.RenderTargetAttachmentCount; ++rt) + { + const auto& RTAttachmentRef = SubpassDesc.pRenderTargetAttachments[rt]; + if (RTAttachmentRef.AttachmentIndex != ATTACHMENT_UNUSED) + { + ppRTVs[rt] = ValidatedCast<TextureViewGLImpl>(m_Desc.ppAttachments[RTAttachmentRef.AttachmentIndex]); + } + } + + if (SubpassDesc.pDepthStencilAttachment != nullptr && SubpassDesc.pDepthStencilAttachment->AttachmentIndex != ATTACHMENT_UNUSED) + { + pDSV = ValidatedCast<TextureViewGLImpl>(m_Desc.ppAttachments[SubpassDesc.pDepthStencilAttachment->AttachmentIndex]); + } + auto FBO = FBOCache::CreateFBO(CtxState, SubpassDesc.RenderTargetAttachmentCount, ppRTVs, pDSV); + m_SubpassFramebuffers.emplace_back(std::move(FBO)); + } } FramebufferGLImpl::~FramebufferGLImpl() diff --git a/Graphics/GraphicsEngineOpenGL/src/RenderDeviceGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/RenderDeviceGLImpl.cpp index 6506c5df..ae77fbcf 100644 --- a/Graphics/GraphicsEngineOpenGL/src/RenderDeviceGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/RenderDeviceGLImpl.cpp @@ -537,7 +537,11 @@ void RenderDeviceGLImpl::CreateFramebuffer(const FramebufferDesc& Desc, IFramebu CreateDeviceObject("Framebuffer", Desc, ppFramebuffer, [&]() // { - FramebufferGLImpl* pFramebufferGL(NEW_RC_OBJ(m_FramebufferAllocator, "FramebufferGLImpl instance", FramebufferGLImpl)(this, Desc)); + auto spDeviceContext = GetImmediateContext(); + VERIFY(spDeviceContext, "Immediate device context has been destroyed"); + auto& GLState = spDeviceContext.RawPtr<DeviceContextGLImpl>()->GetContextState(); + + FramebufferGLImpl* pFramebufferGL(NEW_RC_OBJ(m_FramebufferAllocator, "FramebufferGLImpl instance", FramebufferGLImpl)(this, GLState, Desc)); pFramebufferGL->QueryInterface(IID_Framebuffer, reinterpret_cast<IObject**>(ppFramebuffer)); OnCreateDeviceObject(pFramebufferGL); }); |
