[Feat] (Diligent, MG_State): wire textures, samplers, global UBO, and user framebuffers

- Auto-sync ITextureObject to Diligent textures with dirty-level uploads
- Translate SamplerObject/unit sampler state into Diligent samplers
- Bind the synthesized MGL_GLOBAL_UBO for default-block glUniform data
- Resolve bound draw/read framebuffers to Diligent RTV/DSV for draws/clears/readback
- Wire DrawRangeElements, DrawRangeElementsBaseVertex, MultiDrawArrays, MultiDrawElements
- Add real-texture, uniform, and user-framebuffer tests; 8 Diligent tests pass
This commit is contained in:
BZLZHH
2026-08-23 09:09:03 +08:00
parent 1748da0443
commit 827d46cad3
5 changed files with 996 additions and 35 deletions
+27 -13
View File
@@ -127,13 +127,27 @@ Verified locally on Turnip Adreno 750:
- Color write mask
- Viewport
- Scissor rect
- Texture/sampler full integration:
- `ITextureObject` → Diligent `ITexture` + SRV with automatic dirty upload
- `SamplerObject` / texture-object sampler → Diligent `ISampler`
- Real front-end `glTexImage2D` path (not only `CreateTestTexture`) verified
- Global UBO upload:
- Front-end `glUniform*` shadow → Diligent uniform buffer bound as `MGL_GLOBAL_UBO`
- User framebuffer mapping:
- Current draw/read FBO resolves texture attachments to Diligent RTV/DSV
- `ReadPixels` can read back from a user FBO color attachment
- More GL entry points wired:
- `DrawRangeElements`
- `DrawRangeElementsBaseVertex`
- `MultiDrawArrays`
- `MultiDrawElements`
- Primitive expansion:
- `GL_TRIANGLE_FAN` expanded to triangle list
- `GL_LINE_LOOP` expanded to line strip
- Local test result:
```
[ PASSED ] 5 tests
[ PASSED ] 8 tests
```
---
@@ -180,9 +194,9 @@ Notes:
## 7. Known Limitations / Not Yet Implemented
- `DrawArrays`/`DrawElements` draw through real `MG_State`, but only the color offscreen target is used; no user-created framebuffer/renderbuffer mapping yet.
- Textures: only a test texture path (`CreateTestTexture`) is wired; no automatic sync of `TextureObject` to Diligent resources, no texture unit bindings, no mipmap/sampler state from `MG_State`.
- Uniforms / UBOs are not uploaded or bound yet.
- User framebuffers now support texture color attachments and depth/stencil texture or renderbuffer attachments; renderbuffer readback and multi-target color attachments remain partial.
- Textures auto-sync `ITextureObject` Diligent resources, including mip levels and sampler state; compressed textures and integer/3-channel formats that Diligent lacks are still skipped.
- Global UBO (default-block `glUniform*`) now uploads and binds; named application UBO blocks and SSBOs are not fed from frontend buffer bindings yet.
- No swapchain / EGL window surface presentation yet; `Present()` only flushes.
- No transform feedback / queries / sync / readback of non-color resources.
- Some GL 3.2 entry points are still not implemented in the backend (draw range, multi-draw, blit, buffer subdata paths, etc.).
@@ -194,19 +208,19 @@ Notes:
## 8. Recommended Next Steps
1. **Framebuffer / Renderbuffer mapping**
- Map `MG_State::GLState::FramebufferObject` attachments to Diligent `ITextureView` / `ITexture`.
- Support default framebuffer as current offscreen target.
- Support `glBindFramebuffer`, `glFramebufferTexture2D`, renderbuffer color/depth attachments.
- [x] Map `MG_State::GLState::FramebufferObject` attachments to Diligent `ITextureView` / `ITexture`.
- [x] Support default framebuffer as current offscreen target.
- [~] Support `glBindFramebuffer`, `glFramebufferTexture2D`, renderbuffer color/depth attachments (texture color + depth/renderbuffer work; renderbuffer readback and multiple color targets still partial).
2. **Texture / Sampler full integration**
- Translate MobileGL `ITextureObject` to Diligent `ITexture` and cache by `GetLifetimeId()`.
- Propagate texture unit bindings into the PSO SRB.
- Translate `SamplerObject` state into Diligent `SamplerDesc`.
- [x] Translate MobileGL `ITextureObject` to Diligent `ITexture` and cache by `GetLifetimeId()`.
- [x] Propagate texture unit bindings into the PSO SRB.
- [x] Translate `SamplerObject` state into Diligent `SamplerDesc`.
3. **Uniform / UBO support**
- Create Diligent buffer for `ProgramObject::GetUBOData()` / `GetUBOSize()`.
- Bind the global UBO as a static shader resource.
- Handle per-program uniform block bindings.
- [x] Create Diligent buffer for `ProgramObject::GetUBOData()` / `GetUBOSize()`.
- [x] Bind the global UBO as a dynamic shader resource.
- [ ] Handle per-program uniform block bindings / named UBO blocks.
4. **PSO / resource caching**
- Cache PSOs by program + VAO config + render state + topology.
@@ -63,6 +63,49 @@ namespace MobileGL::MG_Backend::DiligentBackend {
}
}
void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
const void* indices) {
// The CPU-side UploadVertexDataFromState path already honors the selected index
// range. start/end only restrict which indices may be referenced; they do not
// change the vertex buffer layout for this backend.
(void)start;
(void)end;
DrawElements(mode, count, type, indices);
}
void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
const void* indices, GLint basevertex) {
(void)start;
(void)end;
(void)basevertex;
DrawElements(mode, count, type, indices);
}
void MultiDrawArrays(GLenum mode, const GLint* first, const GLsizei* count, GLsizei drawcount) {
auto* renderer = GetActiveRenderer();
if (renderer == nullptr) {
return;
}
for (GLsizei i = 0; i < drawcount; ++i) {
if (count[i] > 0) {
renderer->DrawFromState(mode, first[i], count[i], 0, nullptr);
}
}
}
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
GLsizei drawcount) {
auto* renderer = GetActiveRenderer();
if (renderer == nullptr) {
return;
}
for (GLsizei i = 0; i < drawcount; ++i) {
if (count[i] > 0) {
renderer->DrawFromState(mode, 0, count[i], type, indices[i]);
}
}
}
void Present() {
auto* renderer = GetActiveRenderer();
if (renderer != nullptr) {
@@ -151,6 +194,10 @@ namespace MobileGL::MG_Backend::DiligentBackend {
m_functions.GL.Clear = Clear;
m_functions.GL.DrawArrays = DrawArrays;
m_functions.GL.DrawElements = DrawElements;
m_functions.GL.DrawRangeElements = DrawRangeElements;
m_functions.GL.DrawRangeElementsBaseVertex = DrawRangeElementsBaseVertex;
m_functions.GL.MultiDrawArrays = MultiDrawArrays;
m_functions.GL.MultiDrawElements = MultiDrawElements;
m_functions.Present = Present;
m_initialized = true;
@@ -14,6 +14,8 @@
#include <PipelineState.h>
#include <InputLayout.h>
#include <Sampler.h>
#include <ShaderResourceBinding.h>
#include <ShaderResourceVariable.h>
#include <MG_State/GLState/Core.h>
#include <MG_State/GLState/ProgramState/ProgramObject.h>
@@ -180,6 +182,163 @@ void main()
default: return ::Diligent::STENCIL_OP_KEEP;
}
}
Bool IsDepthFormat(TextureInternalFormat format) {
switch (format) {
case TextureInternalFormat::DepthComponent:
case TextureInternalFormat::DepthComponent16:
case TextureInternalFormat::DepthComponent24:
case TextureInternalFormat::DepthComponent32:
case TextureInternalFormat::DepthComponent32F:
case TextureInternalFormat::Depth24Stencil8:
case TextureInternalFormat::Depth32FStencil8:
case TextureInternalFormat::DepthStencil:
return true;
default:
return false;
}
}
::Diligent::TEXTURE_FORMAT ConvertInternalFormatToDiligent(TextureInternalFormat format) {
switch (format) {
case TextureInternalFormat::R8: return ::Diligent::TEX_FORMAT_R8_UNORM;
case TextureInternalFormat::R8Snorm: return ::Diligent::TEX_FORMAT_R8_SNORM;
case TextureInternalFormat::R8I: return ::Diligent::TEX_FORMAT_R8_SINT;
case TextureInternalFormat::R8UI: return ::Diligent::TEX_FORMAT_R8_UINT;
case TextureInternalFormat::R16: return ::Diligent::TEX_FORMAT_R16_UNORM;
case TextureInternalFormat::R16Snorm: return ::Diligent::TEX_FORMAT_R16_SNORM;
case TextureInternalFormat::R16I: return ::Diligent::TEX_FORMAT_R16_SINT;
case TextureInternalFormat::R16UI: return ::Diligent::TEX_FORMAT_R16_UINT;
case TextureInternalFormat::R16F: return ::Diligent::TEX_FORMAT_R16_FLOAT;
case TextureInternalFormat::R32I: return ::Diligent::TEX_FORMAT_R32_SINT;
case TextureInternalFormat::R32UI: return ::Diligent::TEX_FORMAT_R32_UINT;
case TextureInternalFormat::R32F: return ::Diligent::TEX_FORMAT_R32_FLOAT;
case TextureInternalFormat::RG8: return ::Diligent::TEX_FORMAT_RG8_UNORM;
case TextureInternalFormat::RG8Snorm: return ::Diligent::TEX_FORMAT_RG8_SNORM;
case TextureInternalFormat::RG8I: return ::Diligent::TEX_FORMAT_RG8_SINT;
case TextureInternalFormat::RG8UI: return ::Diligent::TEX_FORMAT_RG8_UINT;
case TextureInternalFormat::RG16: return ::Diligent::TEX_FORMAT_RG16_UNORM;
case TextureInternalFormat::RG16Snorm: return ::Diligent::TEX_FORMAT_RG16_SNORM;
case TextureInternalFormat::RG16I: return ::Diligent::TEX_FORMAT_RG16_SINT;
case TextureInternalFormat::RG16UI: return ::Diligent::TEX_FORMAT_RG16_UINT;
case TextureInternalFormat::RG16F: return ::Diligent::TEX_FORMAT_RG16_FLOAT;
case TextureInternalFormat::RG32I: return ::Diligent::TEX_FORMAT_RG32_SINT;
case TextureInternalFormat::RG32UI: return ::Diligent::TEX_FORMAT_RG32_UINT;
case TextureInternalFormat::RG32F: return ::Diligent::TEX_FORMAT_RG32_FLOAT;
case TextureInternalFormat::RGB8:
case TextureInternalFormat::RGB8I:
case TextureInternalFormat::RGB8UI:
case TextureInternalFormat::RGB16F:
// Diligent does not expose unsized/three-channel non-ETC2 RGB formats.
// RGB32 is the only three-channel format available.
return ::Diligent::TEX_FORMAT_UNKNOWN;
case TextureInternalFormat::RGB32F: return ::Diligent::TEX_FORMAT_RGB32_FLOAT;
case TextureInternalFormat::RGBA8: return ::Diligent::TEX_FORMAT_RGBA8_UNORM;
case TextureInternalFormat::RGBA8Snorm: return ::Diligent::TEX_FORMAT_RGBA8_SNORM;
case TextureInternalFormat::RGBA8I: return ::Diligent::TEX_FORMAT_RGBA8_SINT;
case TextureInternalFormat::RGBA8UI: return ::Diligent::TEX_FORMAT_RGBA8_UINT;
case TextureInternalFormat::RGBA16: return ::Diligent::TEX_FORMAT_RGBA16_UNORM;
case TextureInternalFormat::RGBA16Snorm: return ::Diligent::TEX_FORMAT_RGBA16_SNORM;
case TextureInternalFormat::RGBA16I: return ::Diligent::TEX_FORMAT_RGBA16_SINT;
case TextureInternalFormat::RGBA16UI: return ::Diligent::TEX_FORMAT_RGBA16_UINT;
case TextureInternalFormat::RGBA16F: return ::Diligent::TEX_FORMAT_RGBA16_FLOAT;
case TextureInternalFormat::RGBA32I: return ::Diligent::TEX_FORMAT_RGBA32_SINT;
case TextureInternalFormat::RGBA32UI: return ::Diligent::TEX_FORMAT_RGBA32_UINT;
case TextureInternalFormat::RGBA32F: return ::Diligent::TEX_FORMAT_RGBA32_FLOAT;
case TextureInternalFormat::SRGB8: return ::Diligent::TEX_FORMAT_UNKNOWN;
case TextureInternalFormat::SRGB8Alpha8: return ::Diligent::TEX_FORMAT_RGBA8_UNORM_SRGB;
case TextureInternalFormat::R11FG11FB10F: return ::Diligent::TEX_FORMAT_R11G11B10_FLOAT;
case TextureInternalFormat::RGB9E5: return ::Diligent::TEX_FORMAT_RGB9E5_SHAREDEXP;
case TextureInternalFormat::RGB10A2: return ::Diligent::TEX_FORMAT_RGB10A2_UNORM;
case TextureInternalFormat::RGB10A2UI: return ::Diligent::TEX_FORMAT_RGB10A2_UINT;
case TextureInternalFormat::DepthComponent:
case TextureInternalFormat::DepthComponent32:
case TextureInternalFormat::DepthComponent32F:
return ::Diligent::TEX_FORMAT_D32_FLOAT;
case TextureInternalFormat::DepthComponent16:
return ::Diligent::TEX_FORMAT_D16_UNORM;
case TextureInternalFormat::DepthComponent24:
return ::Diligent::TEX_FORMAT_D24_UNORM_S8_UINT;
case TextureInternalFormat::Depth24Stencil8:
case TextureInternalFormat::DepthStencil:
return ::Diligent::TEX_FORMAT_D24_UNORM_S8_UINT;
case TextureInternalFormat::Depth32FStencil8:
return ::Diligent::TEX_FORMAT_D32_FLOAT_S8X24_UINT;
case TextureInternalFormat::Red:
case TextureInternalFormat::RGBA: // The frontend keeps unsized RGBA as RGBA8 in practice.
return ::Diligent::TEX_FORMAT_RGBA8_UNORM;
case TextureInternalFormat::RG:
return ::Diligent::TEX_FORMAT_RG8_UNORM;
case TextureInternalFormat::RGB:
return ::Diligent::TEX_FORMAT_UNKNOWN;
default:
return ::Diligent::TEX_FORMAT_UNKNOWN;
}
}
::Diligent::RESOURCE_DIMENSION ConvertTextureTargetToDiligent(TextureTarget target) {
switch (target) {
case TextureTarget::Texture1D: return ::Diligent::RESOURCE_DIM_TEX_1D;
case TextureTarget::Texture1DArray: return ::Diligent::RESOURCE_DIM_TEX_1D_ARRAY;
case TextureTarget::Texture2D:
case TextureTarget::TextureRectangle:
case TextureTarget::Texture2DMultisample:
return ::Diligent::RESOURCE_DIM_TEX_2D;
case TextureTarget::Texture2DArray:
case TextureTarget::Texture2DMultisampleArray:
return ::Diligent::RESOURCE_DIM_TEX_2D_ARRAY;
case TextureTarget::Texture3D: return ::Diligent::RESOURCE_DIM_TEX_3D;
case TextureTarget::TextureCubeMap: return ::Diligent::RESOURCE_DIM_TEX_CUBE;
case TextureTarget::TextureCubeMapArray: return ::Diligent::RESOURCE_DIM_TEX_CUBE_ARRAY;
default: return ::Diligent::RESOURCE_DIM_UNDEFINED;
}
}
::Diligent::TEXTURE_ADDRESS_MODE ConvertWrapMode(SamplerWrapMode mode) {
switch (mode) {
case SamplerWrapMode::ClampToEdge: return ::Diligent::TEXTURE_ADDRESS_CLAMP;
case SamplerWrapMode::MirroredRepeat: return ::Diligent::TEXTURE_ADDRESS_MIRROR;
case SamplerWrapMode::Repeat: return ::Diligent::TEXTURE_ADDRESS_WRAP;
case SamplerWrapMode::ClampToBorder: return ::Diligent::TEXTURE_ADDRESS_BORDER;
case SamplerWrapMode::MirrorClampToEdge: return ::Diligent::TEXTURE_ADDRESS_MIRROR_ONCE;
default: return ::Diligent::TEXTURE_ADDRESS_WRAP;
}
}
::Diligent::COMPARISON_FUNCTION ConvertCompareFunc(SamplerCompareFunc func) {
switch (func) {
case SamplerCompareFunc::Never: return ::Diligent::COMPARISON_FUNC_NEVER;
case SamplerCompareFunc::Less: return ::Diligent::COMPARISON_FUNC_LESS;
case SamplerCompareFunc::Equal: return ::Diligent::COMPARISON_FUNC_EQUAL;
case SamplerCompareFunc::LessEqual: return ::Diligent::COMPARISON_FUNC_LESS_EQUAL;
case SamplerCompareFunc::Greater: return ::Diligent::COMPARISON_FUNC_GREATER;
case SamplerCompareFunc::NotEqual: return ::Diligent::COMPARISON_FUNC_NOT_EQUAL;
case SamplerCompareFunc::GreaterEqual: return ::Diligent::COMPARISON_FUNC_GREATER_EQUAL;
case SamplerCompareFunc::Always: return ::Diligent::COMPARISON_FUNC_ALWAYS;
default: return ::Diligent::COMPARISON_FUNC_ALWAYS;
}
}
::Diligent::FILTER_TYPE ConvertFilterType(SamplerFilterMode filter, SamplerMipmapMode mipmap,
Bool comparison, Float maxAnisotropy) {
if (maxAnisotropy > 1.0f) {
return comparison ? ::Diligent::FILTER_TYPE_COMPARISON_ANISOTROPIC
: ::Diligent::FILTER_TYPE_ANISOTROPIC;
}
const Bool linearFilter = filter == SamplerFilterMode::Linear;
const Bool linearMip = mipmap == SamplerMipmapMode::Linear;
const Bool pointMip = mipmap == SamplerMipmapMode::Nearest || mipmap == SamplerMipmapMode::None;
if (!comparison) {
if (linearFilter) {
return linearMip ? ::Diligent::FILTER_TYPE_LINEAR : ::Diligent::FILTER_TYPE_POINT;
}
return pointMip ? ::Diligent::FILTER_TYPE_POINT : ::Diligent::FILTER_TYPE_LINEAR;
}
if (linearFilter) {
return linearMip ? ::Diligent::FILTER_TYPE_COMPARISON_LINEAR : ::Diligent::FILTER_TYPE_COMPARISON_POINT;
}
return pointMip ? ::Diligent::FILTER_TYPE_COMPARISON_POINT : ::Diligent::FILTER_TYPE_COMPARISON_LINEAR;
}
} // namespace
DiligentRenderer::DiligentRenderer(::Diligent::IRenderDevice* device, ::Diligent::IDeviceContext* context)
@@ -345,22 +504,34 @@ void main()
}
void DiligentRenderer::Clear(Float r, Float g, Float b, Float a) {
if (!m_initialized || !m_pContext || !m_pColorRTV) {
if (!m_initialized || !m_pContext) {
return;
}
::Diligent::ITextureView* rtvs[] = {m_pColorRTV};
m_pContext->SetRenderTargets(1, rtvs, m_pDepthDSV, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
Vector<::Diligent::ITextureView*> rtvs;
::Diligent::ITextureView* dsv = nullptr;
if (!ResolveCurrentRenderTargets(rtvs, dsv) || rtvs.empty()) {
return;
}
m_pContext->SetRenderTargets(static_cast<::Diligent::Uint32>(rtvs.size()), rtvs.data(), dsv,
::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
const Float clearColor[] = {r, g, b, a};
m_pContext->ClearRenderTarget(m_pColorRTV, clearColor, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
for (auto* rtv : rtvs) {
m_pContext->ClearRenderTarget(rtv, clearColor, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
}
}
void DiligentRenderer::ClearDepth(Float depth) {
if (!m_initialized || !m_pContext || !m_pDepthDSV) {
if (!m_initialized || !m_pContext) {
return;
}
::Diligent::ITextureView* rtvs[] = {m_pColorRTV};
m_pContext->SetRenderTargets(1, rtvs, m_pDepthDSV, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
m_pContext->ClearDepthStencil(m_pDepthDSV, ::Diligent::CLEAR_DEPTH_FLAG, depth, 0,
Vector<::Diligent::ITextureView*> rtvs;
::Diligent::ITextureView* dsv = nullptr;
if (!ResolveCurrentRenderTargets(rtvs, dsv) || dsv == nullptr) {
return;
}
m_pContext->SetRenderTargets(static_cast<::Diligent::Uint32>(rtvs.size()), rtvs.data(), dsv,
::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
m_pContext->ClearDepthStencil(dsv, ::Diligent::CLEAR_DEPTH_FLAG, depth, 0,
::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
}
@@ -472,6 +643,449 @@ void main()
return m_pTestSampler != nullptr;
}
::Diligent::ITextureView* DiligentRenderer::SyncTexture(MG_State::GLState::ITextureObject& texture) {
if (m_pDevice == nullptr || m_pContext == nullptr || texture.GetFormat() == TextureInternalFormat::Unknown) {
return nullptr;
}
const auto format = ConvertInternalFormatToDiligent(texture.GetFormat());
const auto dimension = ConvertTextureTargetToDiligent(texture.GetTarget());
if (format == ::Diligent::TEX_FORMAT_UNKNOWN || dimension == ::Diligent::RESOURCE_DIM_UNDEFINED) {
MGLOG_W_ONCE("DiligentRenderer::SyncTexture: unsupported texture target/format (target=%d format=%d)",
static_cast<Int>(texture.GetTarget()), static_cast<Int>(texture.GetFormat()));
return nullptr;
}
const Uint64 lifetimeId = texture.GetLifetimeId();
TextureResource* resource = nullptr;
auto it = m_textureCache.find(lifetimeId);
if (it != m_textureCache.end()) {
resource = &it->second;
}
const Bool needCreate = resource == nullptr || !resource->Texture;
const Bool needUpload = needCreate || resource->ContentVersion != texture.GetContentVersion();
const Bool needViewUpdate = needCreate || resource->ParamsVersion != texture.GetTextureParamsVersion();
if (needCreate) {
TextureResource newResource;
newResource.ContentVersion = texture.GetContentVersion();
newResource.ParamsVersion = texture.GetTextureParamsVersion();
newResource.IsDepth = IsDepthFormat(texture.GetFormat());
::Diligent::TextureDesc texDesc;
texDesc.Name = "MobileGL state texture";
texDesc.Type = dimension;
texDesc.Format = format;
const IntVec3 baseSize = texture.GetBaseSize();
texDesc.Width = static_cast<::Diligent::Uint32>(std::max(baseSize.x(), 1));
texDesc.Height = static_cast<::Diligent::Uint32>(std::max(baseSize.y(), 1));
texDesc.ArraySize = 1;
if (texture.GetTarget() == TextureTarget::TextureCubeMap ||
texture.GetTarget() == TextureTarget::TextureCubeMapArray) {
texDesc.ArraySize = 6;
} else if (texture.GetTarget() == TextureTarget::Texture2DArray ||
texture.GetTarget() == TextureTarget::Texture1DArray) {
texDesc.ArraySize = static_cast<::Diligent::Uint32>(std::max(baseSize.z(), 1));
}
texDesc.MipLevels = 1;
if (auto* mip = AsMipmapTexture(&texture)) {
texDesc.MipLevels = std::max<::Diligent::Uint32>(mip->GetMipmapLevelCount(), 1);
}
texDesc.Usage = ::Diligent::USAGE_DEFAULT;
texDesc.BindFlags = ::Diligent::BIND_SHADER_RESOURCE;
if (newResource.IsDepth) {
texDesc.BindFlags |= ::Diligent::BIND_DEPTH_STENCIL;
} else {
texDesc.BindFlags |= ::Diligent::BIND_RENDER_TARGET;
}
::Diligent::RefCntAutoPtr<::Diligent::ITexture> pTexture;
m_pDevice->CreateTexture(texDesc, nullptr, &pTexture);
if (!pTexture) {
MGLOG_W_ONCE("DiligentRenderer::SyncTexture: failed to create texture (id=%llu)", lifetimeId);
return nullptr;
}
newResource.Texture = pTexture;
newResource.SRV = pTexture->GetDefaultView(::Diligent::TEXTURE_VIEW_SHADER_RESOURCE);
if (!newResource.SRV) {
MGLOG_W_ONCE("DiligentRenderer::SyncTexture: failed to create SRV (id=%llu)", lifetimeId);
return nullptr;
}
if (newResource.IsDepth) {
newResource.DSV = pTexture->GetDefaultView(::Diligent::TEXTURE_VIEW_DEPTH_STENCIL);
} else if ((texDesc.BindFlags & ::Diligent::BIND_RENDER_TARGET) != 0) {
newResource.RTV = pTexture->GetDefaultView(::Diligent::TEXTURE_VIEW_RENDER_TARGET);
}
auto inserted = m_textureCache.emplace(lifetimeId, std::move(newResource));
resource = &inserted.first->second;
} else if (needViewUpdate && resource->Texture) {
// Recreate views if the texture's parameter/shape version moved. The content
// upload below keeps the same ITexture; for now only the default views are used.
resource->ParamsVersion = texture.GetTextureParamsVersion();
}
if (needUpload && resource->Texture) {
auto* mip = AsMipmapTexture(&texture);
if (mip != nullptr) {
const auto& uploadTargets = texture.GetUploadTargets();
for (const auto uploadTarget : uploadTargets) {
Uint32 slice = 0;
if (texture.GetTarget() == TextureTarget::TextureCubeMap &&
uploadTarget >= TextureUploadTarget::CubeMapPositiveX &&
uploadTarget <= TextureUploadTarget::CubeMapNegativeZ) {
slice = static_cast<Uint32>(uploadTarget) -
static_cast<Uint32>(TextureUploadTarget::CubeMapPositiveX);
}
const Uint32 levelCount = mip->GetMipmapLevelCount();
for (Uint32 level = 0; level < levelCount; ++level) {
const IntVec3 levelSize = mip->GetMipmapTexelSize(uploadTarget, level);
if (levelSize.x() <= 0 || levelSize.y() <= 0) {
continue;
}
const SizeT byteSize = mip->GetMipmapByteSize(uploadTarget, level);
if (byteSize == 0) {
continue;
}
const void* data = mip->MapMipmapData(uploadTarget, level);
if (data == nullptr) {
continue;
}
const Uint32 depth = std::max(levelSize.z(), 1);
const Uint32 rowSize = static_cast<Uint32>(
byteSize / static_cast<SizeT>(std::max(levelSize.y(), 1)) / static_cast<SizeT>(depth));
::Diligent::TextureSubResData subResData;
subResData.pData = data;
subResData.Stride = rowSize;
::Diligent::Box dstBox{0, static_cast<::Diligent::Uint32>(levelSize.x()),
0, static_cast<::Diligent::Uint32>(levelSize.y()),
0, depth};
m_pContext->UpdateTexture(resource->Texture, level, slice, dstBox, subResData,
::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION,
::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
mip->MarkStorageDirty(uploadTarget, level, false);
}
}
}
resource->ContentVersion = texture.GetContentVersion();
}
return resource->SRV;
}
::Diligent::ITextureView* DiligentRenderer::SyncTextureForAttachment(MG_State::GLState::ITextureObject& texture,
Bool depth) {
auto* srvOrView = SyncTexture(texture);
if (srvOrView == nullptr) {
return nullptr;
}
auto it = m_textureCache.find(texture.GetLifetimeId());
if (it == m_textureCache.end()) {
return nullptr;
}
TextureResource& resource = it->second;
if (depth) {
return resource.DSV ? resource.DSV.RawPtr() : nullptr;
}
return resource.RTV ? resource.RTV.RawPtr() : nullptr;
}
::Diligent::ITextureView* DiligentRenderer::SyncRenderbuffer(
MG_State::GLState::RenderbufferObject& renderbuffer) {
if (m_pDevice == nullptr || !renderbuffer.IsAllocated()) {
return nullptr;
}
const auto format = ConvertInternalFormatToDiligent(renderbuffer.GetInternalFormat());
if (format == ::Diligent::TEX_FORMAT_UNKNOWN) {
return nullptr;
}
const Bool depth = IsDepthFormat(renderbuffer.GetInternalFormat());
::Diligent::TextureDesc desc;
desc.Name = "MobileGL state renderbuffer";
desc.Type = ::Diligent::RESOURCE_DIM_TEX_2D;
desc.Format = format;
desc.Width = static_cast<::Diligent::Uint32>(renderbuffer.GetWidth());
desc.Height = static_cast<::Diligent::Uint32>(renderbuffer.GetHeight());
desc.MipLevels = 1;
desc.Usage = ::Diligent::USAGE_DEFAULT;
desc.BindFlags = depth ? ::Diligent::BIND_DEPTH_STENCIL : ::Diligent::BIND_RENDER_TARGET;
::Diligent::RefCntAutoPtr<::Diligent::ITexture> pTexture;
m_pDevice->CreateTexture(desc, nullptr, &pTexture);
if (!pTexture) {
return nullptr;
}
const auto viewType = depth ? ::Diligent::TEXTURE_VIEW_DEPTH_STENCIL : ::Diligent::TEXTURE_VIEW_RENDER_TARGET;
return pTexture->GetDefaultView(viewType);
}
Bool DiligentRenderer::ResolveCurrentRenderTargets(Vector<::Diligent::ITextureView*>& rtvs,
::Diligent::ITextureView*& dsv) {
rtvs.clear();
dsv = nullptr;
if (!m_initialized) {
return false;
}
if (MG_State::pGLContext == nullptr) {
if (m_pColorRTV) {
rtvs.push_back(m_pColorRTV.RawPtr());
}
dsv = m_pDepthDSV ? m_pDepthDSV.RawPtr() : nullptr;
return !rtvs.empty();
}
auto drawFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
if (!drawFbo || drawFbo->IsDefaultFramebuffer()) {
if (m_pColorRTV) {
rtvs.push_back(m_pColorRTV.RawPtr());
}
dsv = m_pDepthDSV ? m_pDepthDSV.RawPtr() : nullptr;
return !rtvs.empty();
}
if (!drawFbo->CheckCompleteness()) {
return false;
}
const auto& drawBuffers = drawFbo->GetDrawBuffers();
for (const auto attachmentType : drawBuffers) {
if (attachmentType == FramebufferAttachmentType::None) {
continue;
}
const auto& attachment = drawFbo->GetAttachment(attachmentType);
if (attachment.IsEmpty()) {
continue;
}
::Diligent::ITextureView* view = nullptr;
if (attachment.IsTexture()) {
view = SyncTextureForAttachment(*attachment.GetTexture(), false);
} else if (attachment.IsRenderbuffer()) {
view = SyncRenderbuffer(*attachment.GetRenderbuffer());
}
if (view != nullptr) {
rtvs.push_back(view);
}
}
const auto& depthAttachment = drawFbo->GetAttachment(FramebufferAttachmentType::Depth);
const auto& stencilAttachment = drawFbo->GetAttachment(FramebufferAttachmentType::Stencil);
if (depthAttachment.IsTexture()) {
dsv = SyncTextureForAttachment(*depthAttachment.GetTexture(), true);
} else if (depthAttachment.IsRenderbuffer()) {
dsv = SyncRenderbuffer(*depthAttachment.GetRenderbuffer());
} else if (stencilAttachment.IsTexture()) {
dsv = SyncTextureForAttachment(*stencilAttachment.GetTexture(), true);
} else if (stencilAttachment.IsRenderbuffer()) {
dsv = SyncRenderbuffer(*stencilAttachment.GetRenderbuffer());
}
return !rtvs.empty();
}
::Diligent::ISampler* DiligentRenderer::SyncSampler(const MG_State::GLState::SamplerObject& sampler) {
if (m_pDevice == nullptr) {
return nullptr;
}
const Uint64 lifetimeId = sampler.GetLifetimeId();
auto it = m_samplerCache.find(lifetimeId);
if (it != m_samplerCache.end() && it->second.Sampler && it->second.Version == sampler.GetVersion()) {
return it->second.Sampler;
}
const auto& params = sampler.GetAllSamplerParameters();
::Diligent::SamplerDesc desc;
desc.AddressU = ConvertWrapMode(params.wrapS);
desc.AddressV = ConvertWrapMode(params.wrapT);
desc.AddressW = ConvertWrapMode(params.wrapR);
desc.MipLODBias = params.lodBias;
desc.MaxAnisotropy = static_cast<::Diligent::Uint32>(std::max(0.0f, params.maxAnisotropy));
desc.MinLOD = params.minLod;
desc.MaxLOD = params.maxLod;
desc.ComparisonFunc = ConvertCompareFunc(params.compareFunc);
desc.BorderColor[0] = params.borderColor.x();
desc.BorderColor[1] = params.borderColor.y();
desc.BorderColor[2] = params.borderColor.z();
desc.BorderColor[3] = params.borderColor.w();
const Bool compare = params.compareMode == SamplerCompareMode::CompareToTexture;
desc.MinFilter = ConvertFilterType(params.minFilter, params.mipmapMode, compare, params.maxAnisotropy);
desc.MagFilter = ConvertFilterType(params.magFilter, params.mipmapMode, compare, params.maxAnisotropy);
desc.MipFilter = ConvertFilterType(params.minFilter, params.mipmapMode, compare, params.maxAnisotropy);
::Diligent::RefCntAutoPtr<::Diligent::ISampler> pSampler;
m_pDevice->CreateSampler(desc, &pSampler);
if (!pSampler) {
return nullptr;
}
SamplerResource resource;
resource.Sampler = pSampler;
resource.Version = sampler.GetVersion();
m_samplerCache[lifetimeId] = std::move(resource);
return pSampler;
}
Bool DiligentRenderer::UploadUBOFromState(const MG_State::GLState::ProgramObject& program) {
if (m_pDevice == nullptr || m_pContext == nullptr) {
return false;
}
const Uint uboSize = program.GetUBOSize();
if (uboSize == 0) {
m_pUBO.Release();
m_uboSize = 0;
m_uboContentVersion = 0;
m_uboProgramLifetimeId = 0;
return true;
}
const Bool recreate = !m_pUBO || m_uboProgramLifetimeId != program.GetLifetimeId() ||
m_uboSize != uboSize;
if (recreate) {
m_pUBO.Release();
::Diligent::BufferDesc desc;
desc.Name = "MobileGL state global UBO";
desc.BindFlags = ::Diligent::BIND_UNIFORM_BUFFER;
desc.Size = uboSize;
desc.Usage = ::Diligent::USAGE_DEFAULT;
::Diligent::BufferData initialData;
initialData.pData = program.GetUBOData();
initialData.DataSize = uboSize;
m_pDevice->CreateBuffer(desc, &initialData, &m_pUBO);
if (!m_pUBO) {
MGLOG_E("DiligentRenderer::UploadUBOFromState: failed to create UBO buffer");
return false;
}
m_uboSize = uboSize;
m_uboContentVersion = program.GetUBOContentVersion();
m_uboProgramLifetimeId = program.GetLifetimeId();
} else if (m_uboContentVersion != program.GetUBOContentVersion()) {
m_pContext->UpdateBuffer(m_pUBO, 0, uboSize, program.GetUBOData(),
::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
m_uboContentVersion = program.GetUBOContentVersion();
}
return m_pUBO != nullptr;
}
Bool DiligentRenderer::BindShaderResourcesFromState(const MG_State::GLState::ProgramObject& program) {
if (m_pStateSRB == nullptr) {
return false;
}
const auto& spirvs = program.GetGeneratedSpirv();
for (const auto& spv : spirvs) {
if (spv.empty()) {
continue;
}
SpvReflectShaderModule module{};
if (spvReflectCreateShaderModule(spv.size() * sizeof(unsigned), spv.data(), &module) !=
SPV_REFLECT_RESULT_SUCCESS) {
continue;
}
::Diligent::SHADER_TYPE stage = ::Diligent::SHADER_TYPE_UNKNOWN;
if (!GetSpirvStage(spv, stage)) {
spvReflectDestroyShaderModule(&module);
continue;
}
Uint32 bindingCount = 0;
spvReflectEnumerateDescriptorBindings(&module, &bindingCount, nullptr);
Vector<SpvReflectDescriptorBinding*> bindings(static_cast<SizeT>(bindingCount));
if (bindingCount > 0) {
spvReflectEnumerateDescriptorBindings(&module, &bindingCount, bindings.data());
}
for (Uint32 b = 0; b < bindingCount; ++b) {
const auto* binding = bindings[b];
if (binding == nullptr || binding->name == nullptr) {
continue;
}
if (binding->descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) {
const Int location = program.GetUniformLocation(binding->name);
if (location < 0) {
continue;
}
const Int unit = program.GetUniformSamplerOrImageUnitIndex(static_cast<Uint>(location));
if (unit < 0 || MG_State::pGLContext == nullptr) {
continue;
}
auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit);
TextureTarget target = TextureTarget::Unknown;
switch (binding->image.dim) {
case SpvDim1D: target = TextureTarget::Texture1D; break;
case SpvDim2D: target = binding->image.arrayed ? TextureTarget::Texture2DArray : TextureTarget::Texture2D; break;
case SpvDim3D: target = TextureTarget::Texture3D; break;
case SpvDimCube: target = binding->image.arrayed ? TextureTarget::TextureCubeMapArray : TextureTarget::TextureCubeMap; break;
default: break;
}
if (target == TextureTarget::Unknown) {
continue;
}
auto texture = textureUnit.GetBindingSlot(target).GetBoundObject();
if (!texture || MG_State::GLState::IsUndefinedDefaultTexture(texture.get())) {
// Preserve the legacy test-texture path: when no real front-end
// texture is bound, fall back to CreateTestTexture's SRV.
if (m_pTestSRV) {
if (auto* variable = m_pStateSRB->GetVariableByName(stage, binding->name)) {
variable->Set(m_pTestSRV);
}
}
continue;
}
const MG_State::GLState::SamplerObject* samplerToUse = textureUnit.GetSamplerObject().get();
if (samplerToUse == nullptr) {
samplerToUse = texture->GetSamplerObject().get();
}
const Bool incomplete = samplerToUse == nullptr ||
MG_State::GLState::SamplesAsIncompleteTexture(texture.get(), samplerToUse);
if (incomplete) {
if (m_pTestSRV) {
if (auto* variable = m_pStateSRB->GetVariableByName(stage, binding->name)) {
variable->Set(m_pTestSRV);
}
}
continue;
}
auto* srv = SyncTexture(*texture);
if (srv == nullptr) {
continue;
}
auto* sampler = SyncSampler(*samplerToUse);
if (sampler == nullptr) {
continue;
}
srv->SetSampler(sampler);
auto* variable = m_pStateSRB->GetVariableByName(stage, binding->name);
if (variable != nullptr) {
variable->Set(srv);
}
} else if (binding->descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER) {
// MobileGL routes all default-block uniforms into one synthesized
// MGL_GLOBAL_UBO. SPIRV-Reflect may report an empty block name for the
// synthesized block, so always resolve the Diligent variable by the
// frontend's fixed global-UBO name.
if (!UploadUBOFromState(program) || !m_pUBO) {
continue;
}
auto* variable = m_pStateSRB->GetVariableByName(stage, "MGL_GLOBAL_UBO");
if (variable == nullptr && binding->name != nullptr && binding->name[0] != '\0') {
variable = m_pStateSRB->GetVariableByName(stage, binding->name);
}
if (variable != nullptr) {
variable->Set(m_pUBO);
}
}
}
spvReflectDestroyShaderModule(&module);
}
return true;
}
void DiligentRenderer::DrawFromState(GLenum mode, GLint first, GLsizei count, GLenum type, const void* indices) {
if (!m_initialized || !m_pContext || MG_State::pGLContext == nullptr) {
return;
@@ -483,8 +1097,13 @@ void main()
return;
}
::Diligent::ITextureView* rtvs[] = {m_pColorRTV};
m_pContext->SetRenderTargets(1, rtvs, m_pDepthDSV, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
Vector<::Diligent::ITextureView*> rtvs;
::Diligent::ITextureView* dsv = nullptr;
if (!ResolveCurrentRenderTargets(rtvs, dsv)) {
return;
}
m_pContext->SetRenderTargets(static_cast<::Diligent::Uint32>(rtvs.size()), rtvs.data(), dsv,
::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
auto glViewport = MG_State::pGLContext->GetViewport();
if (glViewport.z() <= 0 || glViewport.w() <= 0) {
@@ -517,6 +1136,10 @@ void main()
::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION,
::Diligent::SET_VERTEX_BUFFERS_FLAG_RESET);
m_pContext->SetPipelineState(m_pPSO);
const auto& drawProgram = MG_State::pGLContext->GetProgramForDraw();
if (drawProgram) {
BindShaderResourcesFromState(*drawProgram);
}
if (MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::StencilTest)) {
const auto& front = MG_State::pGLContext->GetStencilState(StencilFace::Front);
m_pContext->SetStencilRef(static_cast<::Diligent::Uint32>(front.Ref));
@@ -599,8 +1222,10 @@ void main()
auto& graphicsPipeline = psoCI.GraphicsPipeline;
psoDesc.PipelineType = ::Diligent::PIPELINE_TYPE_GRAPHICS;
psoDesc.Name = "MobileGL Diligent state PSO";
psoDesc.ResourceLayout.DefaultVariableType = ::Diligent::SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC;
graphicsPipeline.NumRenderTargets = 1;
graphicsPipeline.RTVFormats[0] = ::Diligent::TEX_FORMAT_RGBA8_UNORM;
graphicsPipeline.DSVFormat = ::Diligent::TEX_FORMAT_D32_FLOAT;
switch (mode) {
case GL_POINTS:
graphicsPipeline.PrimitiveTopology = ::Diligent::PRIMITIVE_TOPOLOGY_POINT_LIST;
@@ -695,16 +1320,12 @@ void main()
}
m_pStateSRB = nullptr;
if (m_pTestSRV) {
auto* staticVar = m_pPSO->GetStaticVariableByName(::Diligent::SHADER_TYPE_PIXEL, "g_Texture");
if (staticVar != nullptr) {
staticVar->Set(m_pTestSRV);
}
m_pPSO->CreateShaderResourceBinding(&m_pStateSRB, false);
if (m_pStateSRB) {
m_pPSO->InitializeStaticSRBResources(m_pStateSRB);
}
m_pPSO->CreateShaderResourceBinding(&m_pStateSRB, true);
if (!m_pStateSRB) {
MGLOG_E("DiligentRenderer: failed to create state SRB");
return false;
}
BindShaderResourcesFromState(*program);
return true;
}
@@ -842,12 +1463,34 @@ void main()
return;
}
::Diligent::RefCntAutoPtr<::Diligent::ITexture> pSrcTexture = m_pColorTarget;
if (MG_State::pGLContext != nullptr) {
auto readFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject();
if (readFbo && !readFbo->IsDefaultFramebuffer()) {
auto readBuffer = readFbo->GetReadBuffer();
if (readBuffer == FramebufferAttachmentType::None) {
readBuffer = FramebufferAttachmentType::Color0;
}
const auto& attachment = readFbo->GetAttachment(readBuffer);
if (attachment.IsTexture()) {
if (SyncTexture(*attachment.GetTexture()) != nullptr) {
auto it = m_textureCache.find(attachment.GetTexture()->GetLifetimeId());
if (it != m_textureCache.end() && it->second.Texture) {
pSrcTexture = it->second.Texture;
}
}
}
// Renderbuffer readback is not wired yet; fall back to the default target.
}
}
const auto& srcDesc = pSrcTexture->GetDesc();
::Diligent::TextureDesc stagingDesc;
stagingDesc.Name = "MobileGL Diligent staging readback";
stagingDesc.Type = ::Diligent::RESOURCE_DIM_TEX_2D;
stagingDesc.Format = ::Diligent::TEX_FORMAT_RGBA8_UNORM;
stagingDesc.Width = m_width;
stagingDesc.Height = m_height;
stagingDesc.Width = srcDesc.Width;
stagingDesc.Height = srcDesc.Height;
stagingDesc.MipLevels = 1;
stagingDesc.Usage = ::Diligent::USAGE_STAGING;
stagingDesc.CPUAccessFlags = ::Diligent::CPU_ACCESS_READ;
@@ -860,7 +1503,7 @@ void main()
}
::Diligent::CopyTextureAttribs copyAttribs{
m_pColorTarget, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION,
pSrcTexture, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION,
pStaging, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION};
copyAttribs.SrcMipLevel = 0;
copyAttribs.DstMipLevel = 0;
@@ -31,6 +31,13 @@ namespace Diligent {
struct IShaderResourceBinding;
}
namespace MobileGL::MG_State::GLState {
class ITextureObject;
class SamplerObject;
class ProgramObject;
class RenderbufferObject;
}
namespace MobileGL::MG_Backend::DiligentBackend {
// Minimal real Diligent renderer used to prove the GL 3.2 basic path:
// clear an offscreen color target, draw a hardcoded triangle, and read
@@ -59,11 +66,34 @@ namespace MobileGL::MG_Backend::DiligentBackend {
::Diligent::IDeviceContext* GetContext() const { return m_pContext; }
private:
struct TextureResource {
::Diligent::RefCntAutoPtr<::Diligent::ITexture> Texture;
::Diligent::RefCntAutoPtr<::Diligent::ITextureView> SRV;
::Diligent::RefCntAutoPtr<::Diligent::ITextureView> RTV;
::Diligent::RefCntAutoPtr<::Diligent::ITextureView> DSV;
Uint64 ContentVersion = 0;
Uint16 ParamsVersion = 0;
Bool IsDepth = false;
};
struct SamplerResource {
::Diligent::RefCntAutoPtr<::Diligent::ISampler> Sampler;
Uint16 Version = 0;
};
Bool CreateOffscreenTargets();
Bool CreatePipeline();
Bool CreateVertexBuffer();
Bool CreatePipelineFromState(GLenum mode);
Bool UploadVertexDataFromState(GLenum mode, GLint first, GLsizei count, GLenum type, const void* indices);
::Diligent::ITextureView* SyncTexture(MG_State::GLState::ITextureObject& texture);
::Diligent::ITextureView* SyncTextureForAttachment(MG_State::GLState::ITextureObject& texture, Bool depth);
::Diligent::ITextureView* SyncRenderbuffer(MG_State::GLState::RenderbufferObject& renderbuffer);
::Diligent::ISampler* SyncSampler(const MG_State::GLState::SamplerObject& sampler);
Bool BindShaderResourcesFromState(const MG_State::GLState::ProgramObject& program);
Bool UploadUBOFromState(const MG_State::GLState::ProgramObject& program);
Bool ResolveCurrentRenderTargets(Vector<::Diligent::ITextureView*>& rtvs,
::Diligent::ITextureView*& dsv);
::Diligent::IRenderDevice* m_pDevice = nullptr;
::Diligent::IDeviceContext* m_pContext = nullptr;
@@ -77,6 +107,13 @@ namespace MobileGL::MG_Backend::DiligentBackend {
::Diligent::RefCntAutoPtr<::Diligent::IShaderResourceBinding> m_pStateSRB;
::Diligent::RefCntAutoPtr<::Diligent::IPipelineState> m_pPSO;
::Diligent::RefCntAutoPtr<::Diligent::IBuffer> m_pVertexBuffer;
::Diligent::RefCntAutoPtr<::Diligent::IBuffer> m_pUBO;
Uint32 m_uboSize = 0;
Uint32 m_uboContentVersion = 0;
Uint64 m_uboProgramLifetimeId = 0;
UnorderedMap<Uint64, TextureResource> m_textureCache;
UnorderedMap<Uint64, SamplerResource> m_samplerCache;
UnorderedMap<Uint32, TextureResource> m_renderbufferCache;
Uint32 m_width = 256;
Uint32 m_height = 256;
Uint32 m_lastDrawVertexCount = 0;
@@ -14,6 +14,8 @@
#include <MG_Impl/GLImpl/Program/GL_Program.h>
#include <MG_Impl/GLImpl/RenderState/GL_RenderState.h>
#include <MG_Impl/GLImpl/VertexArray/GL_VertexArray.h>
#include <MG_Impl/GLImpl/Texture/GL_Texture.h>
#include <MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h>
#include <MG_State/GLState/Core.h>
#include <Init.h>
@@ -275,3 +277,221 @@ void main() { Color = vec4(1.0, 0.0, 0.0, 1.0); }
EXPECT_GT(center[0], 200) << "center should be red from indexed triangle";
EXPECT_LT(center[1], 50) << "center should not be green";
}
TEST(DiligentVulkanBackend, DrawsRealTexturedFromMobileGLState) {
MobileGL::Initialize();
const char* vsSrc = R"(#version 330 core
layout(location = 0) in vec2 Position;
layout(location = 1) in vec2 UV;
out vec2 vUV;
void main() { gl_Position = vec4(Position, 0.0, 1.0); vUV = UV; }
)";
const char* fsSrc = R"(#version 330 core
uniform sampler2D g_Texture;
in vec2 vUV;
out vec4 Color;
void main() { Color = texture(g_Texture, vUV); }
)";
const GLuint vs = CreateShader(GL_VERTEX_SHADER);
ShaderSource(vs, 1, &vsSrc, nullptr);
CompileShader(vs);
const GLuint fs = CreateShader(GL_FRAGMENT_SHADER);
ShaderSource(fs, 1, &fsSrc, nullptr);
CompileShader(fs);
const GLuint program = CreateProgram();
AttachShader(program, vs);
AttachShader(program, fs);
LinkProgram(program);
UseProgram(program);
Viewport(0, 0, 256, 256);
const float vertices[] = {
-0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 1.0f, 0.0f,
0.0f, 0.5f, 0.5f, 1.0f,
};
GLuint vbo = 0;
GenBuffers(1, &vbo);
BindBuffer(GL_ARRAY_BUFFER, vbo);
BufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
GLuint vao = 0;
GenVertexArrays(1, &vao);
BindVertexArray(vao);
EnableVertexAttribArray(0);
VertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 16, nullptr);
EnableVertexAttribArray(1);
VertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 16, reinterpret_cast<const void*>(8));
// Create a real front-end texture object instead of relying on the backend's
// test helper. This exercises the automatic TextureObject -> Diligent sync.
const std::uint8_t redTexture[2 * 2 * 4] = {
255, 0, 0, 255, 255, 0, 0, 255,
255, 0, 0, 255, 255, 0, 0, 255,
};
GLuint tex = 0;
GenTextures(1, &tex);
BindTexture(GL_TEXTURE_2D, tex);
TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, redTexture);
// A single-level 2x2 texture is incomplete for the default mipmapped min filter,
// so choose a non-mipmapped filter to exercise the real TextureObject sync path.
TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
DiligentBackend::BackendObject_Diligent backend;
backend.Initialize();
auto* renderer = backend.GetRenderer();
if (renderer == nullptr) {
GTEST_SKIP() << "No Vulkan adapter available; skipping real-texture state test";
}
renderer->Clear(0.0f, 1.0f, 0.0f, 1.0f);
renderer->DrawFromState(GL_TRIANGLES, 0, 3, 0, nullptr);
renderer->Present();
std::uint8_t center[4] = {};
renderer->ReadPixels(128, 128, 1, 1, center);
EXPECT_GT(center[0], 200) << "center should be red from state texture";
EXPECT_LT(center[1], 50) << "center should not be green";
}
TEST(DiligentVulkanBackend, DrawsUniformFromMobileGLState) {
MobileGL::Initialize();
const char* vsSrc = R"(#version 330 core
layout(location = 0) in vec2 Position;
void main() { gl_Position = vec4(Position, 0.0, 1.0); }
)";
const char* fsSrc = R"(#version 330 core
uniform vec4 u_color;
out vec4 Color;
void main() { Color = u_color; }
)";
const GLuint vs = CreateShader(GL_VERTEX_SHADER);
ShaderSource(vs, 1, &vsSrc, nullptr);
CompileShader(vs);
const GLuint fs = CreateShader(GL_FRAGMENT_SHADER);
ShaderSource(fs, 1, &fsSrc, nullptr);
CompileShader(fs);
const GLuint program = CreateProgram();
AttachShader(program, vs);
AttachShader(program, fs);
LinkProgram(program);
UseProgram(program);
Viewport(0, 0, 256, 256);
const GLint colorLoc = GetUniformLocation(program, "u_color");
ASSERT_GE(colorLoc, 0);
Uniform4f(colorLoc, 0.0f, 0.0f, 1.0f, 1.0f);
const float vertices[] = {
-0.5f, -0.5f,
0.5f, -0.5f,
0.0f, 0.5f,
};
GLuint vbo = 0;
GenBuffers(1, &vbo);
BindBuffer(GL_ARRAY_BUFFER, vbo);
BufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
GLuint vao = 0;
GenVertexArrays(1, &vao);
BindVertexArray(vao);
EnableVertexAttribArray(0);
VertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
DiligentBackend::BackendObject_Diligent backend;
backend.Initialize();
auto* renderer = backend.GetRenderer();
if (renderer == nullptr) {
GTEST_SKIP() << "No Vulkan adapter available; skipping uniform state test";
}
renderer->Clear(0.0f, 1.0f, 0.0f, 1.0f);
renderer->DrawFromState(GL_TRIANGLES, 0, 3, 0, nullptr);
renderer->Present();
std::uint8_t center[4] = {};
renderer->ReadPixels(128, 128, 1, 1, center);
EXPECT_LT(center[0], 50) << "center should not be red";
EXPECT_LT(center[1], 50) << "center should not be green";
EXPECT_GT(center[2], 200) << "center should be blue from uniform";
}
TEST(DiligentVulkanBackend, DrawsToOffscreenFramebufferFromMobileGLState) {
MobileGL::Initialize();
const char* vsSrc = R"(#version 330 core
layout(location = 0) in vec2 Position;
void main() { gl_Position = vec4(Position, 0.0, 1.0); }
)";
const char* fsSrc = R"(#version 330 core
out vec4 Color;
void main() { Color = vec4(1.0, 0.0, 0.0, 1.0); }
)";
const GLuint vs = CreateShader(GL_VERTEX_SHADER);
ShaderSource(vs, 1, &vsSrc, nullptr);
CompileShader(vs);
const GLuint fs = CreateShader(GL_FRAGMENT_SHADER);
ShaderSource(fs, 1, &fsSrc, nullptr);
CompileShader(fs);
const GLuint program = CreateProgram();
AttachShader(program, vs);
AttachShader(program, fs);
LinkProgram(program);
UseProgram(program);
Viewport(0, 0, 256, 256);
const float vertices[] = {
-0.5f, -0.5f,
0.5f, -0.5f,
0.0f, 0.5f,
};
GLuint vbo = 0;
GenBuffers(1, &vbo);
BindBuffer(GL_ARRAY_BUFFER, vbo);
BufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
GLuint vao = 0;
GenVertexArrays(1, &vao);
BindVertexArray(vao);
EnableVertexAttribArray(0);
VertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
// 256x256 color texture attached to a user framebuffer.
GLuint tex = 0;
GenTextures(1, &tex);
BindTexture(GL_TEXTURE_2D, tex);
TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
GLuint fbo = 0;
GenFramebuffers(1, &fbo);
BindFramebuffer(GL_FRAMEBUFFER, fbo);
FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
DiligentBackend::BackendObject_Diligent backend;
backend.Initialize();
auto* renderer = backend.GetRenderer();
if (renderer == nullptr) {
GTEST_SKIP() << "No Vulkan adapter available; skipping framebuffer state test";
}
renderer->Clear(0.0f, 1.0f, 0.0f, 1.0f);
renderer->DrawFromState(GL_TRIANGLES, 0, 3, 0, nullptr);
renderer->Present();
std::uint8_t center[4] = {};
renderer->ReadPixels(128, 128, 1, 1, center);
EXPECT_GT(center[0], 200) << "center should be red in the user framebuffer";
EXPECT_LT(center[1], 50) << "center should not be green";
std::uint8_t corner[4] = {};
renderer->ReadPixels(0, 0, 1, 1, corner);
EXPECT_GT(corner[1], 200) << "corner should remain green after clear";
EXPECT_LT(corner[0], 50) << "corner should not be red";
}