Files
MobileGL/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp
T
BZLZHH 24dfbb41f9 [Fix] (Diligent): fix y inversion and front-face winding in viewport mapping
- Translate OpenGL bottom-left viewport/scissor rects to Diligent top-left origin
- Invert FrontCounterClockwise to compensate for the Y conversion
- Add DrawsTopHalfTriangleFromMobileGLState y-orientation test
2026-08-23 12:25:41 +08:00

2066 lines
96 KiB
C++

// MobileGL - MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp
// Copyright (c) 2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
// https://www.gnu.org/licenses/gpl-3.0.txt
// https://www.gnu.org/licenses/lgpl-3.0.txt
// SPDX-License-Identifier: LGPL-3.0-only
#include "DiligentRenderer.h"
#include <RenderDevice.h>
#include <DeviceContext.h>
#include <Texture.h>
#include <Buffer.h>
#include <Shader.h>
#include <PipelineState.h>
#include <InputLayout.h>
#include <Sampler.h>
#include <SwapChain.h>
#include <NativeWindow.h>
#include <ShaderResourceBinding.h>
#include <ShaderResourceVariable.h>
#include <EngineFactoryVk.h>
#include <MG_State/GLState/Core.h>
#include <MG_State/GLState/ProgramState/ProgramObject.h>
#include <MG_State/GLState/VertexArrayState/VertexArrayObject.h>
#include <MG_State/GLState/BufferState/BufferObject.h>
#include <spirv_reflect.h>
#include <vector>
namespace MobileGL::MG_Backend::DiligentBackend {
namespace {
constexpr Uint32 kTriangleVertexCount = 3;
const char* GetTriangleVS() {
return R"(layout(location = 0) in vec2 Position;
void main()
{
gl_Position = vec4(Position, 0.0, 1.0);
}
)";
}
const char* GetTrianglePS() {
return R"(layout(location = 0) out vec4 Color;
void main()
{
Color = vec4(1.0, 0.0, 0.0, 1.0);
}
)";
}
struct TriangleVertex {
Float X;
Float Y;
};
SizeT GetDataTypeSize(DataType type) {
switch (type) {
case DataType::Int8:
case DataType::Uint8:
return 1;
case DataType::Int16:
case DataType::Uint16:
case DataType::Float16:
return 2;
case DataType::Int32:
case DataType::Uint32:
case DataType::Float32:
case DataType::Fixed32:
case DataType::Int2101010Rev:
case DataType::Uint2101010Rev:
return 4;
case DataType::Float64:
return 8;
default:
return 4;
}
}
::Diligent::VALUE_TYPE GetValueType(DataType type) {
switch (type) {
case DataType::Int8: return ::Diligent::VT_INT8;
case DataType::Uint8: return ::Diligent::VT_UINT8;
case DataType::Int16: return ::Diligent::VT_INT16;
case DataType::Uint16: return ::Diligent::VT_UINT16;
case DataType::Int32: return ::Diligent::VT_INT32;
case DataType::Uint32: return ::Diligent::VT_UINT32;
case DataType::Float16: return ::Diligent::VT_FLOAT16;
case DataType::Float32: return ::Diligent::VT_FLOAT32;
case DataType::Float64: return ::Diligent::VT_FLOAT64;
default: return ::Diligent::VT_FLOAT32;
}
}
Bool GetSpirvStage(const Vector<unsigned>& spv, ::Diligent::SHADER_TYPE& outStage) {
if (spv.empty()) {
return false;
}
SpvReflectShaderModule module{};
if (spvReflectCreateShaderModule(spv.size() * sizeof(unsigned), spv.data(), &module) !=
SPV_REFLECT_RESULT_SUCCESS) {
return false;
}
const SpvReflectShaderStageFlagBits stage = module.shader_stage;
spvReflectDestroyShaderModule(&module);
switch (stage) {
case SPV_REFLECT_SHADER_STAGE_VERTEX_BIT:
outStage = ::Diligent::SHADER_TYPE_VERTEX;
return true;
case SPV_REFLECT_SHADER_STAGE_FRAGMENT_BIT:
outStage = ::Diligent::SHADER_TYPE_PIXEL;
return true;
default:
return false;
}
}
::Diligent::BLEND_FACTOR ConvertBlendFactor(BlendFactor factor) {
switch (factor) {
case BlendFactor::Zero: return ::Diligent::BLEND_FACTOR_ZERO;
case BlendFactor::One: return ::Diligent::BLEND_FACTOR_ONE;
case BlendFactor::SrcColor: return ::Diligent::BLEND_FACTOR_SRC_COLOR;
case BlendFactor::OneMinusSrcColor: return ::Diligent::BLEND_FACTOR_INV_SRC_COLOR;
case BlendFactor::DstColor: return ::Diligent::BLEND_FACTOR_DEST_COLOR;
case BlendFactor::OneMinusDstColor: return ::Diligent::BLEND_FACTOR_INV_DEST_COLOR;
case BlendFactor::SrcAlpha: return ::Diligent::BLEND_FACTOR_SRC_ALPHA;
case BlendFactor::OneMinusSrcAlpha: return ::Diligent::BLEND_FACTOR_INV_SRC_ALPHA;
case BlendFactor::DstAlpha: return ::Diligent::BLEND_FACTOR_DEST_ALPHA;
case BlendFactor::OneMinusDstAlpha: return ::Diligent::BLEND_FACTOR_INV_DEST_ALPHA;
case BlendFactor::ConstantColor: return ::Diligent::BLEND_FACTOR_BLEND_FACTOR;
case BlendFactor::OneMinusConstantColor: return ::Diligent::BLEND_FACTOR_INV_BLEND_FACTOR;
case BlendFactor::Src1Color: return ::Diligent::BLEND_FACTOR_SRC1_COLOR;
case BlendFactor::OneMinusSrc1Color: return ::Diligent::BLEND_FACTOR_INV_SRC1_COLOR;
case BlendFactor::Src1Alpha: return ::Diligent::BLEND_FACTOR_SRC1_ALPHA;
case BlendFactor::OneMinusSrc1Alpha: return ::Diligent::BLEND_FACTOR_INV_SRC1_ALPHA;
default: return ::Diligent::BLEND_FACTOR_ONE;
}
}
::Diligent::BLEND_OPERATION ConvertBlendEquation(BlendEquation equation) {
switch (equation) {
case BlendEquation::Add: return ::Diligent::BLEND_OPERATION_ADD;
case BlendEquation::Subtract: return ::Diligent::BLEND_OPERATION_SUBTRACT;
case BlendEquation::ReverseSubtract: return ::Diligent::BLEND_OPERATION_REV_SUBTRACT;
case BlendEquation::Min: return ::Diligent::BLEND_OPERATION_MIN;
case BlendEquation::Max: return ::Diligent::BLEND_OPERATION_MAX;
default: return ::Diligent::BLEND_OPERATION_ADD;
}
}
::Diligent::COMPARISON_FUNCTION ConvertDepthFunc(DepthTestFunc func) {
switch (func) {
case DepthTestFunc::Never: return ::Diligent::COMPARISON_FUNC_NEVER;
case DepthTestFunc::Less: return ::Diligent::COMPARISON_FUNC_LESS;
case DepthTestFunc::Equal: return ::Diligent::COMPARISON_FUNC_EQUAL;
case DepthTestFunc::LessEqual: return ::Diligent::COMPARISON_FUNC_LESS_EQUAL;
case DepthTestFunc::Greater: return ::Diligent::COMPARISON_FUNC_GREATER;
case DepthTestFunc::NotEqual: return ::Diligent::COMPARISON_FUNC_NOT_EQUAL;
case DepthTestFunc::GreaterEqual: return ::Diligent::COMPARISON_FUNC_GREATER_EQUAL;
case DepthTestFunc::Always: return ::Diligent::COMPARISON_FUNC_ALWAYS;
default: return ::Diligent::COMPARISON_FUNC_ALWAYS;
}
}
::Diligent::CULL_MODE ConvertCullMode(CullFaceMode mode) {
switch (mode) {
case CullFaceMode::Front: return ::Diligent::CULL_MODE_FRONT;
case CullFaceMode::Back: return ::Diligent::CULL_MODE_BACK;
case CullFaceMode::FrontAndBack: return ::Diligent::CULL_MODE_NONE;
default: return ::Diligent::CULL_MODE_NONE;
}
}
::Diligent::STENCIL_OP ConvertStencilOp(StencilOperation op) {
switch (op) {
case StencilOperation::Keep: return ::Diligent::STENCIL_OP_KEEP;
case StencilOperation::Zero: return ::Diligent::STENCIL_OP_ZERO;
case StencilOperation::Replace: return ::Diligent::STENCIL_OP_REPLACE;
case StencilOperation::IncrementClamp: return ::Diligent::STENCIL_OP_INCR_SAT;
case StencilOperation::DecrementClamp: return ::Diligent::STENCIL_OP_DECR_SAT;
case StencilOperation::Invert: return ::Diligent::STENCIL_OP_INVERT;
case StencilOperation::IncrementWrap: return ::Diligent::STENCIL_OP_INCR_WRAP;
case StencilOperation::DecrementWrap: return ::Diligent::STENCIL_OP_DECR_WRAP;
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)
: m_pDevice(device), m_pContext(context) {}
DiligentRenderer::~DiligentRenderer() {
m_pVertexBuffer.Release();
m_pPSO.Release();
m_pColorRTV.Release();
m_pColorTarget.Release();
m_pDepthDSV.Release();
m_pDepthTarget.Release();
}
Bool DiligentRenderer::Initialize(Uint32 width, Uint32 height) {
if (m_initialized) {
return true;
}
if (m_pDevice == nullptr || m_pContext == nullptr) {
return false;
}
m_width = width > 0 ? width : 256;
m_height = height > 0 ? height : 256;
if (!CreateOffscreenTargets()) {
return false;
}
if (!CreatePipeline()) {
return false;
}
if (!CreateVertexBuffer()) {
return false;
}
m_initialized = true;
return true;
}
Bool DiligentRenderer::CreateSwapChain(::Diligent::IEngineFactoryVk* factory, const WindowHandle& handle,
Uint32 width, Uint32 height) {
if (factory == nullptr || m_pDevice == nullptr || m_pContext == nullptr || handle.Handle == nullptr) {
MGLOG_E("DiligentRenderer::CreateSwapChain: invalid factory/device/context/window");
return false;
}
::Diligent::SwapChainDesc desc;
desc.Width = width > 0 ? width : std::max<Uint32>(handle.Width, 1);
desc.Height = height > 0 ? height : std::max<Uint32>(handle.Height, 1);
desc.ColorBufferFormat = ::Diligent::TEX_FORMAT_RGBA8_UNORM_SRGB;
desc.DepthBufferFormat = ::Diligent::TEX_FORMAT_D24_UNORM_S8_UINT;
desc.BufferCount = 2;
::Diligent::NativeWindow nativeWindow{};
#if PLATFORM_ANDROID
nativeWindow.pAWindow = handle.Handle;
#elif PLATFORM_LINUX
// X11 Window IDs are integers that the frontend stores as a void* handle.
// The X11 display/connection could not be plumbed through WindowHandle yet;
// swapchain creation on X11 may need those fields filled by a future change.
nativeWindow.WindowId = static_cast<::Diligent::Uint32>(
reinterpret_cast<uintptr_t>(handle.Handle));
#else
(void)nativeWindow;
#endif
::Diligent::RefCntAutoPtr<::Diligent::ISwapChain> pSwapChain;
factory->CreateSwapChainVk(m_pDevice, m_pContext, desc, nativeWindow, &pSwapChain);
if (!pSwapChain) {
MGLOG_E("DiligentRenderer::CreateSwapChain: failed to create swap chain");
return false;
}
m_pSwapChain = pSwapChain;
m_width = desc.Width;
m_height = desc.Height;
return true;
}
Bool DiligentRenderer::ResizeSwapChain(Uint32 width, Uint32 height) {
if (m_pSwapChain == nullptr) {
return false;
}
m_pSwapChain->Resize(width > 0 ? width : 1, height > 0 ? height : 1);
m_width = width > 0 ? width : m_width;
m_height = height > 0 ? height : m_height;
return true;
}
Bool DiligentRenderer::CreateOffscreenTargets() {
::Diligent::TextureDesc texDesc;
texDesc.Name = "MobileGL Diligent offscreen color target";
texDesc.Type = ::Diligent::RESOURCE_DIM_TEX_2D;
texDesc.Format = ::Diligent::TEX_FORMAT_RGBA8_UNORM;
texDesc.Width = m_width;
texDesc.Height = m_height;
texDesc.MipLevels = 1;
texDesc.BindFlags = ::Diligent::BIND_RENDER_TARGET | ::Diligent::BIND_SHADER_RESOURCE;
texDesc.Usage = ::Diligent::USAGE_DEFAULT;
::Diligent::RefCntAutoPtr<::Diligent::ITexture> pColorTarget;
m_pDevice->CreateTexture(texDesc, nullptr, &pColorTarget);
if (!pColorTarget) {
MGLOG_E("DiligentRenderer: failed to create offscreen color target");
return false;
}
m_pColorTarget = pColorTarget;
m_pColorRTV = m_pColorTarget->GetDefaultView(::Diligent::TEXTURE_VIEW_RENDER_TARGET);
if (!m_pColorRTV) {
MGLOG_E("DiligentRenderer: failed to get offscreen RTV");
return false;
}
::Diligent::TextureDesc depthDesc;
depthDesc.Name = "MobileGL Diligent offscreen depth target";
depthDesc.Type = ::Diligent::RESOURCE_DIM_TEX_2D;
depthDesc.Format = ::Diligent::TEX_FORMAT_D24_UNORM_S8_UINT;
depthDesc.Width = m_width;
depthDesc.Height = m_height;
depthDesc.MipLevels = 1;
depthDesc.BindFlags = ::Diligent::BIND_DEPTH_STENCIL;
depthDesc.Usage = ::Diligent::USAGE_DEFAULT;
::Diligent::RefCntAutoPtr<::Diligent::ITexture> pDepthTarget;
m_pDevice->CreateTexture(depthDesc, nullptr, &pDepthTarget);
if (!pDepthTarget) {
MGLOG_E("DiligentRenderer: failed to create offscreen depth target");
return false;
}
m_pDepthTarget = pDepthTarget;
m_pDepthDSV = m_pDepthTarget->GetDefaultView(::Diligent::TEXTURE_VIEW_DEPTH_STENCIL);
if (!m_pDepthDSV) {
MGLOG_E("DiligentRenderer: failed to get offscreen DSV");
return false;
}
return true;
}
Bool DiligentRenderer::CreatePipeline() {
::Diligent::GraphicsPipelineStateCreateInfo psoCI;
auto& psoDesc = psoCI.PSODesc;
auto& graphicsPipeline = psoCI.GraphicsPipeline;
psoDesc.PipelineType = ::Diligent::PIPELINE_TYPE_GRAPHICS;
psoDesc.Name = "MobileGL Diligent triangle PSO";
graphicsPipeline.NumRenderTargets = 1;
graphicsPipeline.RTVFormats[0] = ::Diligent::TEX_FORMAT_RGBA8_UNORM;
graphicsPipeline.PrimitiveTopology = ::Diligent::PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
graphicsPipeline.RasterizerDesc.CullMode = ::Diligent::CULL_MODE_NONE;
graphicsPipeline.DepthStencilDesc.DepthEnable = false;
::Diligent::LayoutElement layoutElems[] = {
{0, 0, 2, ::Diligent::VT_FLOAT32},
};
graphicsPipeline.InputLayout.LayoutElements = layoutElems;
graphicsPipeline.InputLayout.NumElements = 1;
::Diligent::ShaderCreateInfo shaderCI;
shaderCI.SourceLanguage = ::Diligent::SHADER_SOURCE_LANGUAGE_GLSL;
shaderCI.EntryPoint = "main";
::Diligent::RefCntAutoPtr<::Diligent::IShader> pVS;
shaderCI.Desc = {"MobileGL Diligent triangle VS", ::Diligent::SHADER_TYPE_VERTEX, true};
shaderCI.Source = GetTriangleVS();
m_pDevice->CreateShader(shaderCI, &pVS);
if (!pVS) {
MGLOG_E("DiligentRenderer: failed to create vertex shader");
return false;
}
::Diligent::RefCntAutoPtr<::Diligent::IShader> pPS;
shaderCI.Desc = {"MobileGL Diligent triangle PS", ::Diligent::SHADER_TYPE_PIXEL, true};
shaderCI.Source = GetTrianglePS();
m_pDevice->CreateShader(shaderCI, &pPS);
if (!pPS) {
MGLOG_E("DiligentRenderer: failed to create pixel shader");
return false;
}
psoCI.pVS = pVS;
psoCI.pPS = pPS;
m_pDevice->CreateGraphicsPipelineState(psoCI, &m_pPSO);
if (!m_pPSO) {
MGLOG_E("DiligentRenderer: failed to create pipeline state");
return false;
}
return true;
}
Bool DiligentRenderer::CreateVertexBuffer() {
const TriangleVertex vertices[] = {
{-0.5f, -0.5f},
{ 0.5f, -0.5f},
{ 0.0f, 0.5f},
};
::Diligent::BufferDesc buffDesc;
buffDesc.Name = "MobileGL Diligent triangle vertex buffer";
buffDesc.BindFlags = ::Diligent::BIND_VERTEX_BUFFER;
buffDesc.Size = sizeof(vertices);
::Diligent::BufferData initialData;
initialData.pData = vertices;
initialData.DataSize = sizeof(vertices);
m_pDevice->CreateBuffer(buffDesc, &initialData, &m_pVertexBuffer);
if (!m_pVertexBuffer) {
MGLOG_E("DiligentRenderer: failed to create vertex buffer");
return false;
}
return true;
}
void DiligentRenderer::Clear(Float r, Float g, Float b, Float a) {
if (!m_initialized || !m_pContext) {
return;
}
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};
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) {
return;
}
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);
}
void DiligentRenderer::ClearStencil(Uint32 stencil) {
if (!m_initialized || !m_pContext) {
return;
}
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_STENCIL_FLAG, 0, stencil,
::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
}
void DiligentRenderer::DrawTriangle() {
if (!m_initialized || !m_pContext || !m_pPSO) {
return;
}
::Diligent::ITextureView* rtvs[] = {m_pColorRTV};
m_pContext->SetRenderTargets(1, rtvs, m_pDepthDSV, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
::Diligent::Viewport viewport{0, 0, static_cast<Float>(m_width), static_cast<Float>(m_height), 0.0f, 1.0f};
m_pContext->SetViewports(1, &viewport, m_width, m_height);
::Diligent::IBuffer* pVBs[] = {m_pVertexBuffer};
m_pContext->SetVertexBuffers(0, 1, pVBs, nullptr,
::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION,
::Diligent::SET_VERTEX_BUFFERS_FLAG_RESET);
m_pContext->SetPipelineState(m_pPSO);
::Diligent::DrawAttribs drawAttrs;
drawAttrs.NumVertices = kTriangleVertexCount;
drawAttrs.Flags = ::Diligent::DRAW_FLAG_VERIFY_ALL;
m_pContext->Draw(drawAttrs);
}
void DiligentRenderer::DrawVertices(const Float* vertices, Uint32 vertexCount) {
if (!m_initialized || !m_pContext || !m_pPSO || vertices == nullptr || vertexCount == 0) {
return;
}
const Uint64 dataSize = static_cast<Uint64>(vertexCount) * 2 * sizeof(Float);
if (dataSize > m_pVertexBuffer->GetDesc().Size) {
::Diligent::BufferDesc buffDesc;
buffDesc.Name = "MobileGL Diligent dynamic vertex buffer";
buffDesc.BindFlags = ::Diligent::BIND_VERTEX_BUFFER;
buffDesc.Size = dataSize;
::Diligent::BufferData initialData;
initialData.pData = vertices;
initialData.DataSize = static_cast<Uint32>(dataSize);
m_pVertexBuffer.Release();
m_pDevice->CreateBuffer(buffDesc, &initialData, &m_pVertexBuffer);
if (!m_pVertexBuffer) {
MGLOG_E("DiligentRenderer: failed to create dynamic vertex buffer");
return;
}
} else {
m_pContext->UpdateBuffer(m_pVertexBuffer, 0, dataSize, vertices,
::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
}
::Diligent::ITextureView* rtvs[] = {m_pColorRTV};
m_pContext->SetRenderTargets(1, rtvs, m_pDepthDSV, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
::Diligent::Viewport viewport{0, 0, static_cast<Float>(m_width), static_cast<Float>(m_height), 0.0f, 1.0f};
m_pContext->SetViewports(1, &viewport, m_width, m_height);
::Diligent::IBuffer* pVBs[] = {m_pVertexBuffer};
m_pContext->SetVertexBuffers(0, 1, pVBs, nullptr,
::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION,
::Diligent::SET_VERTEX_BUFFERS_FLAG_RESET);
m_pContext->SetPipelineState(m_pPSO);
::Diligent::DrawAttribs drawAttrs;
drawAttrs.NumVertices = vertexCount;
drawAttrs.Flags = ::Diligent::DRAW_FLAG_VERIFY_ALL;
m_pContext->Draw(drawAttrs);
}
Bool DiligentRenderer::CreateTestTexture(const void* data, Uint32 width, Uint32 height) {
if (m_pDevice == nullptr || data == nullptr || width == 0 || height == 0) {
return false;
}
::Diligent::TextureDesc texDesc;
texDesc.Name = "MobileGL Diligent test texture";
texDesc.Type = ::Diligent::RESOURCE_DIM_TEX_2D;
texDesc.Format = ::Diligent::TEX_FORMAT_RGBA8_UNORM;
texDesc.Width = width;
texDesc.Height = height;
texDesc.MipLevels = 1;
texDesc.BindFlags = ::Diligent::BIND_SHADER_RESOURCE;
texDesc.Usage = ::Diligent::USAGE_DEFAULT;
::Diligent::TextureSubResData subResData;
subResData.pData = data;
subResData.Stride = static_cast<::Diligent::Uint64>(width) * 4;
::Diligent::TextureData texData;
texData.pSubResources = &subResData;
texData.NumSubresources = 1;
m_pDevice->CreateTexture(texDesc, &texData, &m_pTestTexture);
if (!m_pTestTexture) {
return false;
}
m_pTestSRV = m_pTestTexture->GetDefaultView(::Diligent::TEXTURE_VIEW_SHADER_RESOURCE);
if (!m_pTestSRV) {
return false;
}
::Diligent::SamplerDesc samplerDesc;
m_pDevice->CreateSampler(samplerDesc, &m_pTestSampler);
if (m_pTestSampler) {
m_pTestSRV->SetSampler(m_pTestSampler);
}
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;
}
texDesc.MiscFlags = ::Diligent::MISC_TEXTURE_FLAG_GENERATE_MIPS;
::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());
auto cachedIt = m_renderbufferCache.find(renderbuffer.GetExternalIndex());
if (cachedIt != m_renderbufferCache.end() && cachedIt->second.Texture) {
const auto& cachedDesc = cachedIt->second.Texture->GetDesc();
if (cachedDesc.Width == static_cast<::Diligent::Uint32>(renderbuffer.GetWidth()) &&
cachedDesc.Height == static_cast<::Diligent::Uint32>(renderbuffer.GetHeight()) &&
cachedDesc.Format == format) {
return depth ? cachedIt->second.DSV.RawPtr() : cachedIt->second.RTV.RawPtr();
}
}
::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;
}
TextureResource resource;
resource.Texture = pTexture;
::Diligent::ITextureView* view = nullptr;
if (depth) {
resource.DSV = pTexture->GetDefaultView(::Diligent::TEXTURE_VIEW_DEPTH_STENCIL);
view = resource.DSV.RawPtr();
} else {
resource.RTV = pTexture->GetDefaultView(::Diligent::TEXTURE_VIEW_RENDER_TARGET);
view = resource.RTV.RawPtr();
}
m_renderbufferCache[renderbuffer.GetExternalIndex()] = std::move(resource);
return view;
}
Bool DiligentRenderer::ResolveCurrentRenderTargets(Vector<::Diligent::ITextureView*>& rtvs,
::Diligent::ITextureView*& dsv) {
rtvs.clear();
dsv = nullptr;
if (!m_initialized) {
return false;
}
auto useSwapChainTargets = [&]() -> Bool {
if (m_pSwapChain && m_pSwapChain->GetCurrentBackBufferRTV()) {
rtvs.push_back(m_pSwapChain->GetCurrentBackBufferRTV());
dsv = m_pSwapChain->GetDepthBufferDSV();
return true;
}
return false;
};
if (MG_State::pGLContext == nullptr) {
if (useSwapChainTargets()) {
return true;
}
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 (useSwapChainTargets()) {
return true;
}
if (m_pColorRTV) {
rtvs.push_back(m_pColorRTV.RawPtr());
}
dsv = m_pDepthDSV ? m_pDepthDSV.RawPtr() : nullptr;
return !rtvs.empty();
}
// Keep the completeness check advisory: some front-end FBOs (e.g. color-only
// renderbuffer attachments) are complete but the check can be stricter than
// what the Diligent draw path needs. Missing attachments still resolve to no
// render targets below.
(void)drawFbo->CheckCompleteness();
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) {
const char* blockName = binding->block.name != nullptr && binding->block.name[0] != '\0'
? binding->block.name
: (binding->name != nullptr && binding->name[0] != '\0'
? binding->name
: (binding->type_description != nullptr &&
binding->type_description->type_name != nullptr
? binding->type_description->type_name
: ""));
const Bool isGlobal = blockName == nullptr || blockName[0] == '\0' ||
std::strstr(blockName, "MGL_GLOBAL_UBO") != nullptr;
if (isGlobal) {
// SPIRV-Reflect may report an empty block name for the synthesized
// global UBO; resolve it by the frontend's fixed 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);
}
continue;
}
// Named application uniform block: resolve the GL block index, read
// the frontend buffer currently bound at the block's binding point,
// and upload its mapped range to a Diligent uniform buffer.
const Uint blockIndex = program.GetUniformBlockIndex(blockName);
if (blockIndex == 0xFFFFFFFFu) {
continue;
}
const Uint frontendBinding = program.GetUniformBlockBinding(blockIndex);
if (MG_State::pGLContext == nullptr ||
frontendBinding >= MG_State::pGLContext->GetBufferBindingPointCount(BufferTarget::Uniform)) {
continue;
}
auto& bindingPoint = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::Uniform,
frontendBinding);
const auto& bufferObject = bindingPoint.GetBoundObject();
if (!bufferObject) {
continue;
}
bufferObject->SyncPersistentMappedRange();
const Uint8* data = bufferObject->MappedData();
if (data == nullptr) {
continue;
}
const auto range = bindingPoint.GetRange();
if (range.end <= range.start) {
continue;
}
const SizeT rangeSize = range.end - range.start;
auto& cached = m_namedUboCache[bufferObject->GetLifetimeId()];
if (!cached) {
::Diligent::BufferDesc desc;
desc.Name = "MobileGL state named UBO";
desc.BindFlags = ::Diligent::BIND_UNIFORM_BUFFER;
desc.Size = rangeSize;
desc.Usage = ::Diligent::USAGE_DEFAULT;
::Diligent::BufferData initialData;
initialData.pData = data + range.start;
initialData.DataSize = static_cast<::Diligent::Uint32>(rangeSize);
m_pDevice->CreateBuffer(desc, &initialData, &cached);
if (!cached) {
continue;
}
} else {
const SizeT bufferSize = static_cast<SizeT>(
std::min<::Diligent::Uint64>(cached->GetDesc().Size, static_cast<::Diligent::Uint64>(rangeSize)));
m_pContext->UpdateBuffer(cached, 0, bufferSize, data + range.start,
::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
}
auto* variable = m_pStateSRB->GetVariableByName(stage, blockName);
if (variable == nullptr && binding->name != nullptr && binding->name[0] != '\0') {
variable = m_pStateSRB->GetVariableByName(stage, binding->name);
}
if (variable != nullptr) {
variable->Set(cached);
}
}
}
spvReflectDestroyShaderModule(&module);
}
return true;
}
void DiligentRenderer::DrawFromState(GLenum mode, GLint first, GLsizei count, GLenum type, const void* indices,
GLint baseVertex) {
if (!m_initialized || !m_pContext || MG_State::pGLContext == nullptr) {
return;
}
if (!CreatePipelineFromState(mode)) {
return;
}
if (!UploadVertexDataFromState(mode, first, count, type, indices, baseVertex)) {
return;
}
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) {
glViewport = IntVec4(0, 0, static_cast<Int32>(m_width), static_cast<Int32>(m_height));
}
// Diligent uses the top-left window origin (Direct3D convention); OpenGL uses
// a bottom-left origin. Translate the GL viewport rect into Diligent's space.
const Float viewportY = static_cast<Float>(m_height) - (glViewport.w() + glViewport.y());
::Diligent::Viewport viewport{
static_cast<Float>(glViewport.x()),
viewportY,
static_cast<Float>(glViewport.z()),
static_cast<Float>(glViewport.w()),
0.0f, 1.0f};
m_pContext->SetViewports(1, &viewport, m_width, m_height);
if (MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::ScissorTest)) {
const auto scissor = MG_State::pGLContext->GetScissorBox();
const Int32 scissorTop = static_cast<Int32>(m_height) - (scissor.w() + scissor.y());
::Diligent::Rect rect{
scissor.x(),
scissorTop,
scissor.x() + scissor.z(),
scissorTop + scissor.w()};
m_pContext->SetScissorRects(1, &rect, m_width, m_height);
} else {
::Diligent::Rect rect{0, 0, static_cast<::Diligent::Int32>(m_width),
static_cast<::Diligent::Int32>(m_height)};
m_pContext->SetScissorRects(1, &rect, m_width, m_height);
}
::Diligent::IBuffer* pVBs[] = {m_pVertexBuffer};
m_pContext->SetVertexBuffers(0, 1, pVBs, nullptr,
::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));
}
if (m_pStateSRB) {
m_pContext->CommitShaderResources(m_pStateSRB, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
}
::Diligent::DrawAttribs drawAttrs;
drawAttrs.NumVertices = m_lastDrawVertexCount;
drawAttrs.Flags = ::Diligent::DRAW_FLAG_VERIFY_ALL;
m_pContext->Draw(drawAttrs);
}
Bool DiligentRenderer::CreatePipelineFromState(GLenum mode) {
if (MG_State::pGLContext == nullptr) {
return false;
}
const auto& program = MG_State::pGLContext->GetProgramForDraw();
if (!program || !program->GetSpirvStatus()) {
return false;
}
// Resolve the current draw-FBO color attachment set so the PSO is created with
// matching RTV formats/count.
Uint32 rtCount = 1;
::Diligent::TEXTURE_FORMAT rtFormats[8] = {};
rtFormats[0] = ::Diligent::TEX_FORMAT_RGBA8_UNORM;
if (MG_State::pGLContext != nullptr) {
auto drawFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
if (drawFbo && !drawFbo->IsDefaultFramebuffer()) {
rtCount = 0;
for (const auto attachmentType : drawFbo->GetDrawBuffers()) {
if (attachmentType == FramebufferAttachmentType::None) {
continue;
}
const auto& attachment = drawFbo->GetAttachment(attachmentType);
::Diligent::TEXTURE_FORMAT fmt = ::Diligent::TEX_FORMAT_UNKNOWN;
if (attachment.IsTexture()) {
fmt = ConvertInternalFormatToDiligent(attachment.GetTexture()->GetFormat());
} else if (attachment.IsRenderbuffer()) {
fmt = ConvertInternalFormatToDiligent(attachment.GetRenderbuffer()->GetInternalFormat());
}
if (fmt != ::Diligent::TEX_FORMAT_UNKNOWN && rtCount < 8) {
rtFormats[rtCount++] = fmt;
}
}
if (rtCount == 0) {
rtCount = 1;
rtFormats[0] = ::Diligent::TEX_FORMAT_RGBA8_UNORM;
}
}
}
// Cheap last-PSO cache: the pipeline depends only on the program, the
// render-state subset baked into the PSO, the primitive topology and the
// enabled vertex-attribute layout. Textures/UBOs are bound dynamically, so
// they do not participate in this key.
const auto& cachedVao = *MG_State::pGLContext->GetBoundVertexArray();
const auto& cachedAttributes = cachedVao.GetAllAttributes();
Uint64 psoKey = program->GetLifetimeId();
psoKey = psoKey * 1099511628211ull + MG_State::pGLContext->GetPipelineStateVersion();
psoKey = psoKey * 1099511628211ull + static_cast<Uint64>(mode);
psoKey = psoKey * 1099511628211ull + rtCount;
for (Uint32 i = 0; i < rtCount; ++i) {
psoKey = psoKey * 1099511628211ull + static_cast<Uint64>(rtFormats[i]);
}
for (Uint32 i = 0; i < cachedVao.MAX_VERTEX_ATTRIBS; ++i) {
const auto& attr = cachedAttributes[i];
if (!attr.Enabled) {
continue;
}
psoKey = psoKey * 1099511628211ull + i;
psoKey = psoKey * 1099511628211ull + static_cast<Uint64>(attr.Size);
psoKey = psoKey * 1099511628211ull + static_cast<Uint64>(attr.Type);
psoKey = psoKey * 1099511628211ull + (attr.Normalized ? 1ull : 0ull);
}
if (m_hasCachedPSO && m_lastPSOKey == psoKey && m_pPSO && m_pStateSRB) {
return true;
}
const auto& spirvs = program->GetGeneratedSpirv();
::Diligent::RefCntAutoPtr<::Diligent::IShader> pVS;
::Diligent::RefCntAutoPtr<::Diligent::IShader> pPS;
for (const auto& spv : spirvs) {
::Diligent::SHADER_TYPE stage = ::Diligent::SHADER_TYPE_UNKNOWN;
if (!GetSpirvStage(spv, stage)) {
continue;
}
::Diligent::ShaderCreateInfo shaderCI;
shaderCI.ByteCode = spv.data();
shaderCI.ByteCodeSize = spv.size() * sizeof(unsigned);
shaderCI.Desc = {"MobileGL Diligent state shader", stage, true};
::Diligent::RefCntAutoPtr<::Diligent::IShader> pShader;
m_pDevice->CreateShader(shaderCI, &pShader);
if (!pShader) {
MGLOG_E("DiligentRenderer: failed to create state shader");
return false;
}
if (stage == ::Diligent::SHADER_TYPE_VERTEX) {
pVS = pShader;
} else if (stage == ::Diligent::SHADER_TYPE_PIXEL) {
pPS = pShader;
}
}
if (!pVS || !pPS) {
MGLOG_W("DiligentRenderer: state program has no vertex/pixel SPIR-V");
return false;
}
const auto& vao = *MG_State::pGLContext->GetBoundVertexArray();
const auto& attributes = vao.GetAllAttributes();
Vector<::Diligent::LayoutElement> layoutElements;
Vector<Uint32> activeAttribs;
for (Uint32 i = 0; i < vao.MAX_VERTEX_ATTRIBS; ++i) {
const auto& attr = attributes[i];
if (!attr.Enabled) {
continue;
}
::Diligent::LayoutElement elem{};
elem.InputIndex = i;
elem.BufferSlot = 0;
elem.NumComponents = static_cast<::Diligent::Uint32>(attr.Size);
elem.ValueType = GetValueType(attr.Type);
elem.IsNormalized = attr.Normalized;
activeAttribs.push_back(i);
layoutElements.push_back(elem);
}
if (layoutElements.empty()) {
MGLOG_W("DiligentRenderer: no enabled vertex attributes");
return false;
}
::Diligent::GraphicsPipelineStateCreateInfo psoCI;
auto& psoDesc = psoCI.PSODesc;
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 = static_cast<::Diligent::Uint8>(rtCount);
for (Uint32 i = 0; i < rtCount; ++i) {
graphicsPipeline.RTVFormats[i] = rtFormats[i];
}
graphicsPipeline.DSVFormat = ::Diligent::TEX_FORMAT_D24_UNORM_S8_UINT;
switch (mode) {
case GL_POINTS:
graphicsPipeline.PrimitiveTopology = ::Diligent::PRIMITIVE_TOPOLOGY_POINT_LIST;
break;
case GL_LINES:
graphicsPipeline.PrimitiveTopology = ::Diligent::PRIMITIVE_TOPOLOGY_LINE_LIST;
break;
case GL_LINE_STRIP:
graphicsPipeline.PrimitiveTopology = ::Diligent::PRIMITIVE_TOPOLOGY_LINE_STRIP;
break;
case GL_TRIANGLES:
graphicsPipeline.PrimitiveTopology = ::Diligent::PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
break;
case GL_TRIANGLE_STRIP:
graphicsPipeline.PrimitiveTopology = ::Diligent::PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
break;
default:
// GL_TRIANGLE_FAN and GL_LINE_LOOP are emulated in UploadVertexData.
graphicsPipeline.PrimitiveTopology = ::Diligent::PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
break;
}
const Bool blendEnabled = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::Blend);
const Bool depthEnabled = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::DepthTest);
const Bool cullEnabled = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::CullFace);
const Bool scissorEnabled = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::ScissorTest);
(void)scissorEnabled;
if (blendEnabled) {
BlendFactor srcRGB = BlendFactor::One;
BlendFactor dstRGB = BlendFactor::Zero;
BlendFactor srcAlpha = BlendFactor::One;
BlendFactor dstAlpha = BlendFactor::Zero;
BlendEquation eqRGB = BlendEquation::Add;
BlendEquation eqAlpha = BlendEquation::Add;
MG_State::pGLContext->GetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha);
MG_State::pGLContext->GetBlendEquation(eqRGB, eqAlpha);
auto& rtBlend = graphicsPipeline.BlendDesc.RenderTargets[0];
rtBlend.BlendEnable = true;
rtBlend.SrcBlend = ConvertBlendFactor(srcRGB);
rtBlend.DestBlend = ConvertBlendFactor(dstRGB);
rtBlend.BlendOp = ConvertBlendEquation(eqRGB);
rtBlend.SrcBlendAlpha = ConvertBlendFactor(srcAlpha);
rtBlend.DestBlendAlpha = ConvertBlendFactor(dstAlpha);
rtBlend.BlendOpAlpha = ConvertBlendEquation(eqAlpha);
}
graphicsPipeline.RasterizerDesc.CullMode = cullEnabled ? ConvertCullMode(MG_State::pGLContext->GetCullFaceMode())
: ::Diligent::CULL_MODE_NONE;
// The viewport Y conversion mirrors GL's bottom-left origin to Diligent's
// top-left origin, so the front-face winding must be inverted to match GL.
graphicsPipeline.RasterizerDesc.FrontCounterClockwise =
MG_State::pGLContext->GetFrontFaceMode() == FrontFaceMode::Clockwise;
graphicsPipeline.DepthStencilDesc.DepthEnable = depthEnabled;
graphicsPipeline.DepthStencilDesc.DepthWriteEnable = MG_State::pGLContext->GetDepthMask();
graphicsPipeline.DepthStencilDesc.DepthFunc = ConvertDepthFunc(MG_State::pGLContext->GetDepthFunc());
const Bool stencilEnabled = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::StencilTest);
if (stencilEnabled) {
const auto& front = MG_State::pGLContext->GetStencilState(StencilFace::Front);
const auto& back = MG_State::pGLContext->GetStencilState(StencilFace::Back);
auto& dsDesc = graphicsPipeline.DepthStencilDesc;
dsDesc.StencilEnable = true;
dsDesc.StencilReadMask = static_cast<::Diligent::Uint8>(front.ValueMask & 0xFFu);
dsDesc.StencilWriteMask = static_cast<::Diligent::Uint8>(front.WriteMask & 0xFFu);
dsDesc.FrontFace.StencilFailOp = ConvertStencilOp(front.FailOp);
dsDesc.FrontFace.StencilDepthFailOp = ConvertStencilOp(front.PassDepthFailOp);
dsDesc.FrontFace.StencilPassOp = ConvertStencilOp(front.PassDepthPassOp);
dsDesc.FrontFace.StencilFunc = ConvertDepthFunc(front.Func);
dsDesc.BackFace.StencilFailOp = ConvertStencilOp(back.FailOp);
dsDesc.BackFace.StencilDepthFailOp = ConvertStencilOp(back.PassDepthFailOp);
dsDesc.BackFace.StencilPassOp = ConvertStencilOp(back.PassDepthPassOp);
dsDesc.BackFace.StencilFunc = ConvertDepthFunc(back.Func);
}
const auto colorMask = MG_State::pGLContext->GetColorMask();
auto& rtBlend = graphicsPipeline.BlendDesc.RenderTargets[0];
rtBlend.RenderTargetWriteMask = ::Diligent::COLOR_MASK_NONE;
if (colorMask.x()) rtBlend.RenderTargetWriteMask |= ::Diligent::COLOR_MASK_RED;
if (colorMask.y()) rtBlend.RenderTargetWriteMask |= ::Diligent::COLOR_MASK_GREEN;
if (colorMask.z()) rtBlend.RenderTargetWriteMask |= ::Diligent::COLOR_MASK_BLUE;
if (colorMask.w()) rtBlend.RenderTargetWriteMask |= ::Diligent::COLOR_MASK_ALPHA;
graphicsPipeline.InputLayout.LayoutElements = layoutElements.data();
graphicsPipeline.InputLayout.NumElements = static_cast<::Diligent::Uint32>(layoutElements.size());
psoCI.pVS = pVS;
psoCI.pPS = pPS;
m_pPSO.Release();
m_pDevice->CreateGraphicsPipelineState(psoCI, &m_pPSO);
if (!m_pPSO) {
MGLOG_E("DiligentRenderer: failed to create state pipeline");
return false;
}
m_pStateSRB = nullptr;
m_pPSO->CreateShaderResourceBinding(&m_pStateSRB, true);
if (!m_pStateSRB) {
MGLOG_E("DiligentRenderer: failed to create state SRB");
return false;
}
BindShaderResourcesFromState(*program);
m_lastPSOKey = psoKey;
m_hasCachedPSO = true;
return true;
}
Bool DiligentRenderer::UploadVertexDataFromState(GLenum mode, GLint first, GLsizei count, GLenum type,
const void* indices, GLint baseVertex) {
if (MG_State::pGLContext == nullptr) {
return false;
}
const auto& vao = *MG_State::pGLContext->GetBoundVertexArray();
const auto& attributes = vao.GetAllAttributes();
Vector<Uint32> activeAttribs;
for (Uint32 i = 0; i < vao.MAX_VERTEX_ATTRIBS; ++i) {
if (attributes[i].Enabled) {
activeAttribs.push_back(i);
}
}
if (activeAttribs.empty()) {
return false;
}
// Resolve the vertex index list (DrawArrays first..first+count or
// DrawElements index buffer/client memory).
Vector<Uint32> indicesData;
const Uint32 vertexCount = static_cast<Uint32>(count);
if (mode == GL_TRIANGLE_FAN) {
// Expand a triangle fan into a triangle list.
indicesData.reserve((vertexCount - 2) * 3);
for (Uint32 i = 1; i + 1 < vertexCount; ++i) {
indicesData.push_back(0);
indicesData.push_back(i);
indicesData.push_back(i + 1);
}
} else if (mode == GL_LINE_LOOP) {
indicesData.reserve(vertexCount + 1);
for (Uint32 i = 0; i < vertexCount; ++i) {
indicesData.push_back(i);
}
if (vertexCount > 0) {
indicesData.push_back(0);
}
} else if (type != 0 || indices != nullptr) {
// DrawElements path.
const auto& indexSlot = vao.GetIndexBufferBindingSlot();
const auto& indexBuffer = indexSlot.GetBoundObject();
const Uint8* indexBase = nullptr;
if (indexBuffer) {
indexBuffer->SyncPersistentMappedRange();
indexBase = indexBuffer->MappedData();
if (indexBase == nullptr) {
return false;
}
indexBase += reinterpret_cast<SizeT>(indices);
} else {
indexBase = static_cast<const Uint8*>(indices);
if (indexBase == nullptr) {
return false;
}
}
const SizeT indexSize = GetDataTypeSize(
type == GL_UNSIGNED_BYTE ? DataType::Uint8 :
type == GL_UNSIGNED_SHORT ? DataType::Uint16 : DataType::Uint32);
indicesData.reserve(vertexCount);
for (Uint32 i = 0; i < vertexCount; ++i) {
if (indexSize == 1) {
indicesData.push_back(indexBase[i]);
} else if (indexSize == 2) {
indicesData.push_back(reinterpret_cast<const Uint16*>(indexBase)[i]);
} else {
indicesData.push_back(reinterpret_cast<const Uint32*>(indexBase)[i]);
}
}
}
const Uint32 drawVertexCount = indicesData.empty() ? vertexCount : static_cast<Uint32>(indicesData.size());
if (drawVertexCount == 0) {
return false;
}
// Pack enabled attributes into a single interleaved vertex buffer.
SizeT vertexStride = 0;
for (Uint32 attrIndex : activeAttribs) {
vertexStride += GetDataTypeSize(attributes[attrIndex].Type) * static_cast<SizeT>(attributes[attrIndex].Size);
}
Vector<Uint8> vertexData(static_cast<SizeT>(drawVertexCount) * vertexStride);
for (Uint32 vi = 0; vi < drawVertexCount; ++vi) {
Uint32 srcIndex = 0;
if (indicesData.empty()) {
srcIndex = static_cast<Uint32>(first) + vi;
} else {
const Int64 resolvedIndex = static_cast<Int64>(indicesData[vi]) + baseVertex;
if (resolvedIndex < 0) {
return false;
}
srcIndex = static_cast<Uint32>(resolvedIndex);
}
Uint8* dst = vertexData.data() + static_cast<SizeT>(vi) * vertexStride;
for (Uint32 attrIndex : activeAttribs) {
const auto& attr = attributes[attrIndex];
if (!attr.Buffer) {
return false;
}
attr.Buffer->SyncPersistentMappedRange();
const Uint8* src = attr.Buffer->MappedData();
if (src == nullptr) {
return false;
}
const SizeT elemSize = GetDataTypeSize(attr.Type) * static_cast<SizeT>(attr.Size);
const SizeT srcOffset = attr.Offset + static_cast<SizeT>(srcIndex) * static_cast<SizeT>(attr.Stride);
if (srcOffset + elemSize > attr.Buffer->GetSize()) {
return false;
}
std::memcpy(dst, src + srcOffset, elemSize);
dst += elemSize;
}
}
if (!m_pVertexBuffer || m_pVertexBuffer->GetDesc().Size < vertexData.size()) {
::Diligent::BufferDesc buffDesc;
buffDesc.Name = "MobileGL Diligent state vertex buffer";
buffDesc.BindFlags = ::Diligent::BIND_VERTEX_BUFFER;
buffDesc.Size = vertexData.size();
::Diligent::BufferData initialData;
initialData.pData = vertexData.data();
initialData.DataSize = static_cast<::Diligent::Uint32>(vertexData.size());
m_pVertexBuffer.Release();
m_pDevice->CreateBuffer(buffDesc, &initialData, &m_pVertexBuffer);
if (!m_pVertexBuffer) {
return false;
}
} else {
m_pContext->UpdateBuffer(m_pVertexBuffer, 0, vertexData.size(), vertexData.data(),
::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
}
m_lastDrawVertexCount = drawVertexCount;
return true;
}
void DiligentRenderer::ReadPixels(Uint32 x, Uint32 y, Uint32 width, Uint32 height, void* pixels) {
if (!m_initialized || !m_pContext || !m_pColorTarget || pixels == nullptr) {
return;
}
::Diligent::RefCntAutoPtr<::Diligent::ITexture> pSrcTexture = m_pColorTarget;
if (m_pSwapChain && m_pSwapChain->GetCurrentBackBufferRTV()) {
pSrcTexture = m_pSwapChain->GetCurrentBackBufferRTV()->GetTexture();
}
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;
}
}
} else if (attachment.IsRenderbuffer()) {
if (SyncRenderbuffer(*attachment.GetRenderbuffer()) != nullptr) {
auto it = m_renderbufferCache.find(attachment.GetRenderbuffer()->GetExternalIndex());
if (it != m_renderbufferCache.end() && it->second.Texture) {
pSrcTexture = it->second.Texture;
}
}
}
}
}
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 = srcDesc.Width;
stagingDesc.Height = srcDesc.Height;
stagingDesc.MipLevels = 1;
stagingDesc.Usage = ::Diligent::USAGE_STAGING;
stagingDesc.CPUAccessFlags = ::Diligent::CPU_ACCESS_READ;
::Diligent::RefCntAutoPtr<::Diligent::ITexture> pStaging;
m_pDevice->CreateTexture(stagingDesc, nullptr, &pStaging);
if (!pStaging) {
MGLOG_E("DiligentRenderer: failed to create staging texture");
return;
}
::Diligent::CopyTextureAttribs copyAttribs{
pSrcTexture, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION,
pStaging, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION};
copyAttribs.SrcMipLevel = 0;
copyAttribs.DstMipLevel = 0;
copyAttribs.SrcSlice = 0;
copyAttribs.DstSlice = 0;
m_pContext->CopyTexture(copyAttribs);
m_pContext->WaitForIdle();
::Diligent::MappedTextureSubresource mapped;
m_pContext->MapTextureSubresource(pStaging, 0, 0, ::Diligent::MAP_READ,
::Diligent::MAP_FLAG_DO_NOT_WAIT, nullptr, mapped);
if (mapped.pData == nullptr) {
MGLOG_E("DiligentRenderer: failed to map staging texture");
return;
}
const Uint8* srcBase = static_cast<const Uint8*>(mapped.pData) + y * mapped.Stride + x * 4;
Uint8* dst = static_cast<Uint8*>(pixels);
for (Uint32 row = 0; row < height; ++row) {
std::memcpy(dst + row * width * 4, srcBase + row * mapped.Stride, width * 4);
}
m_pContext->UnmapTextureSubresource(pStaging, 0, 0);
}
void DiligentRenderer::BlitNamedFramebuffer(
const SharedPtr<MG_State::GLState::FramebufferObject>& readFbo,
const SharedPtr<MG_State::GLState::FramebufferObject>& drawFbo,
GLbitfield mask) {
if (!m_initialized || !m_pContext || (mask & GL_COLOR_BUFFER_BIT) == 0) {
return;
}
::Diligent::RefCntAutoPtr<::Diligent::ITexture> pSrcTexture = m_pColorTarget;
::Diligent::RefCntAutoPtr<::Diligent::ITexture> pDstTexture = m_pColorTarget;
if (readFbo && !readFbo->IsDefaultFramebuffer()) {
auto readBuffer = readFbo->GetReadBuffer();
if (readBuffer == FramebufferAttachmentType::None) {
readBuffer = FramebufferAttachmentType::Color0;
}
const auto& att = readFbo->GetAttachment(readBuffer);
if (att.IsTexture() && SyncTexture(*att.GetTexture()) != nullptr) {
auto it = m_textureCache.find(att.GetTexture()->GetLifetimeId());
if (it != m_textureCache.end() && it->second.Texture) {
pSrcTexture = it->second.Texture;
}
} else if (att.IsRenderbuffer() && SyncRenderbuffer(*att.GetRenderbuffer()) != nullptr) {
auto it = m_renderbufferCache.find(att.GetRenderbuffer()->GetExternalIndex());
if (it != m_renderbufferCache.end() && it->second.Texture) {
pSrcTexture = it->second.Texture;
}
}
}
if (drawFbo && !drawFbo->IsDefaultFramebuffer()) {
for (const auto candidate : drawFbo->GetDrawBuffers()) {
if (candidate == FramebufferAttachmentType::None) {
continue;
}
const auto& att = drawFbo->GetAttachment(candidate);
if (att.IsTexture() && SyncTexture(*att.GetTexture()) != nullptr) {
auto it = m_textureCache.find(att.GetTexture()->GetLifetimeId());
if (it != m_textureCache.end() && it->second.Texture) {
pDstTexture = it->second.Texture;
}
} else if (att.IsRenderbuffer() && SyncRenderbuffer(*att.GetRenderbuffer()) != nullptr) {
auto it = m_renderbufferCache.find(att.GetRenderbuffer()->GetExternalIndex());
if (it != m_renderbufferCache.end() && it->second.Texture) {
pDstTexture = it->second.Texture;
}
}
break;
}
}
if (!pSrcTexture || !pDstTexture || pSrcTexture == pDstTexture) {
return;
}
::Diligent::CopyTextureAttribs copyAttribs(
pSrcTexture, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION,
pDstTexture, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
m_pContext->CopyTexture(copyAttribs);
}
void DiligentRenderer::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
GLbitfield mask, GLenum filter) {
(void)srcX0;
(void)srcY0;
(void)srcX1;
(void)srcY1;
(void)dstX0;
(void)dstY0;
(void)dstX1;
(void)dstY1;
(void)filter;
if (!m_initialized || !m_pContext || (mask & GL_COLOR_BUFFER_BIT) == 0) {
return;
}
::Diligent::RefCntAutoPtr<::Diligent::ITexture> pSrcTexture = m_pColorTarget;
::Diligent::RefCntAutoPtr<::Diligent::ITexture> pDstTexture = 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& srcAtt = readFbo->GetAttachment(readBuffer);
if (srcAtt.IsTexture() && SyncTexture(*srcAtt.GetTexture()) != nullptr) {
auto it = m_textureCache.find(srcAtt.GetTexture()->GetLifetimeId());
if (it != m_textureCache.end() && it->second.Texture) {
pSrcTexture = it->second.Texture;
}
}
}
auto drawFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
if (drawFbo && !drawFbo->IsDefaultFramebuffer()) {
const auto& drawBuffers = drawFbo->GetDrawBuffers();
FramebufferAttachmentType dstBuffer = FramebufferAttachmentType::None;
for (const auto candidate : drawBuffers) {
if (candidate != FramebufferAttachmentType::None) {
dstBuffer = candidate;
break;
}
}
if (dstBuffer != FramebufferAttachmentType::None) {
const auto& dstAtt = drawFbo->GetAttachment(dstBuffer);
if (dstAtt.IsTexture() && SyncTexture(*dstAtt.GetTexture()) != nullptr) {
auto it = m_textureCache.find(dstAtt.GetTexture()->GetLifetimeId());
if (it != m_textureCache.end() && it->second.Texture) {
pDstTexture = it->second.Texture;
}
}
}
}
}
if (!pSrcTexture || !pDstTexture || pSrcTexture == pDstTexture) {
return;
}
::Diligent::CopyTextureAttribs copyAttribs(
pSrcTexture, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION,
pDstTexture, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
m_pContext->CopyTexture(copyAttribs);
}
void DiligentRenderer::CopyReadFramebufferToTexture(MG_State::GLState::ITextureObject& dst) {
if (!m_initialized || !m_pContext || !m_pColorTarget) {
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& att = readFbo->GetAttachment(readBuffer);
if (att.IsTexture() && SyncTexture(*att.GetTexture()) != nullptr) {
auto it = m_textureCache.find(att.GetTexture()->GetLifetimeId());
if (it != m_textureCache.end() && it->second.Texture) {
pSrcTexture = it->second.Texture;
}
}
}
}
if (SyncTexture(dst) == nullptr) {
return;
}
auto it = m_textureCache.find(dst.GetLifetimeId());
if (it == m_textureCache.end() || !it->second.Texture) {
return;
}
auto pDstTexture = it->second.Texture;
if (pSrcTexture == pDstTexture) {
return;
}
::Diligent::CopyTextureAttribs copyAttribs(
pSrcTexture, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION,
pDstTexture, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
m_pContext->CopyTexture(copyAttribs);
}
void DiligentRenderer::CopyTextureSubData(MG_State::GLState::ITextureObject& src,
MG_State::GLState::ITextureObject& dst) {
if (!m_initialized || !m_pContext) {
return;
}
if (SyncTexture(src) == nullptr || SyncTexture(dst) == nullptr) {
return;
}
auto srcIt = m_textureCache.find(src.GetLifetimeId());
auto dstIt = m_textureCache.find(dst.GetLifetimeId());
if (srcIt == m_textureCache.end() || dstIt == m_textureCache.end() ||
!srcIt->second.Texture || !dstIt->second.Texture) {
return;
}
if (srcIt->second.Texture == dstIt->second.Texture) {
return;
}
::Diligent::CopyTextureAttribs copyAttribs(
srcIt->second.Texture, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION,
dstIt->second.Texture, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
m_pContext->CopyTexture(copyAttribs);
}
void DiligentRenderer::GenerateMipmap(MG_State::GLState::ITextureObject& texture) {
if (m_pContext == nullptr || SyncTexture(texture) == nullptr) {
return;
}
auto it = m_textureCache.find(texture.GetLifetimeId());
if (it == m_textureCache.end() || !it->second.SRV) {
return;
}
m_pContext->GenerateMips(it->second.SRV);
}
Bool DiligentRenderer::ReadTextureImage(MG_State::GLState::ITextureObject& texture, Uint32 level,
void* pixels) {
if (!m_initialized || !m_pContext || pixels == nullptr) {
return false;
}
if (SyncTexture(texture) == nullptr) {
return false;
}
auto it = m_textureCache.find(texture.GetLifetimeId());
if (it == m_textureCache.end() || !it->second.Texture) {
return false;
}
auto& resource = it->second;
const auto& srcDesc = resource.Texture->GetDesc();
if (srcDesc.Format != ::Diligent::TEX_FORMAT_RGBA8_UNORM &&
srcDesc.Format != ::Diligent::TEX_FORMAT_RGBA8_UNORM_SRGB) {
return false;
}
::Diligent::TextureDesc stagingDesc;
stagingDesc.Name = "MobileGL Diligent texture image staging";
stagingDesc.Type = ::Diligent::RESOURCE_DIM_TEX_2D;
stagingDesc.Format = srcDesc.Format;
stagingDesc.Width = srcDesc.Width;
stagingDesc.Height = srcDesc.Height;
stagingDesc.MipLevels = 1;
stagingDesc.Usage = ::Diligent::USAGE_STAGING;
stagingDesc.CPUAccessFlags = ::Diligent::CPU_ACCESS_READ;
::Diligent::RefCntAutoPtr<::Diligent::ITexture> pStaging;
m_pDevice->CreateTexture(stagingDesc, nullptr, &pStaging);
if (!pStaging) {
return false;
}
::Diligent::CopyTextureAttribs copyAttribs(
resource.Texture, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION,
pStaging, ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
copyAttribs.SrcMipLevel = level;
copyAttribs.DstMipLevel = 0;
m_pContext->CopyTexture(copyAttribs);
m_pContext->WaitForIdle();
::Diligent::MappedTextureSubresource mapped;
m_pContext->MapTextureSubresource(pStaging, 0, 0, ::Diligent::MAP_READ,
::Diligent::MAP_FLAG_DO_NOT_WAIT, nullptr, mapped);
if (mapped.pData == nullptr) {
return false;
}
const Uint32 width = srcDesc.Width >> level;
const Uint32 height = srcDesc.Height >> level;
Uint8* dst = static_cast<Uint8*>(pixels);
for (Uint32 row = 0; row < height; ++row) {
std::memcpy(dst + row * width * 4,
static_cast<const Uint8*>(mapped.pData) + row * mapped.Stride,
width * 4);
}
m_pContext->UnmapTextureSubresource(pStaging, 0, 0);
return true;
}
void DiligentRenderer::SetSwapInterval(Uint32 interval) {
m_swapInterval = interval;
}
void DiligentRenderer::ReleaseSwapChain() {
m_pSwapChain.Release();
}
void DiligentRenderer::Present() {
if (m_pSwapChain) {
m_pSwapChain->Present(m_swapInterval);
} else if (m_pContext) {
// Offscreen renderer: nothing to present yet.
m_pContext->Flush();
}
}
} // namespace MobileGL::MG_Backend::DiligentBackend