mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-09 04:38:30 +09:00
[Feat] (Diligent): add real offscreen renderer with clear and triangle draw
- Add DiligentRenderer: creates an offscreen RGBA8 render target, compiles a GLSL vertex/pixel shader through Diligent's glslang path, creates a triangle vertex buffer and pipeline, and supports clear/draw/readback. - BackendObject_Diligent now owns a DiligentRenderer after device creation. - Extend local sanity test to clear green, draw a red triangle, and verify center is red and corner stays green. - All Diligent local tests pass on Turnip Adreno 750.
This commit is contained in:
@@ -416,6 +416,7 @@ if(MOBILEGL_ENABLE_DILIGENT)
|
||||
list(APPEND SOURCE_FILES
|
||||
MobileGL/MG_Backend/Diligent/BackendObject_Diligent.cpp
|
||||
MobileGL/MG_Backend/Diligent/DiligentVulkan.cpp
|
||||
MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "BackendObject_Diligent.h"
|
||||
#include "DiligentVulkan.h"
|
||||
#include "Renderer/DiligentRenderer.h"
|
||||
#include <MG_Backend/BackendObject.h>
|
||||
|
||||
#include <EngineFactoryVk.h>
|
||||
@@ -32,6 +33,7 @@ namespace MobileGL::MG_Backend::DiligentBackend {
|
||||
: m_rendererInfo(BuildInitialRendererInfo()) {}
|
||||
|
||||
BackendObject_Diligent::~BackendObject_Diligent() {
|
||||
m_pRenderer.reset();
|
||||
m_pContext.Release();
|
||||
m_pDevice.Release();
|
||||
m_pFactoryVk = nullptr;
|
||||
@@ -96,9 +98,20 @@ namespace MobileGL::MG_Backend::DiligentBackend {
|
||||
MGLOG_W("Diligent: backend initialization failed");
|
||||
return;
|
||||
}
|
||||
|
||||
m_pRenderer = std::make_unique<DiligentRenderer>(m_pDevice, m_pContext);
|
||||
if (!m_pRenderer->Initialize(256, 256)) {
|
||||
MGLOG_W("Diligent: renderer initialization failed");
|
||||
m_pRenderer.reset();
|
||||
return;
|
||||
}
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
DiligentRenderer* BackendObject_Diligent::GetRenderer() {
|
||||
return m_pRenderer.get();
|
||||
}
|
||||
|
||||
Bool BackendObject_Diligent::InitCapabilities() {
|
||||
// Skeleton: no format probing yet. The backend advertises GL 3.2 core
|
||||
// capability, and the capability tables will be filled as resource
|
||||
|
||||
@@ -28,6 +28,8 @@ namespace Diligent {
|
||||
}
|
||||
|
||||
namespace MobileGL::MG_Backend::DiligentBackend {
|
||||
class DiligentRenderer;
|
||||
|
||||
// New Diligent/Vulkan backend, implemented from scratch on top of
|
||||
// DiligentCore. The backend object owns the Diligent device/context and
|
||||
// currently advertises OpenGL 3.2 core capability; the GL function table
|
||||
@@ -47,6 +49,8 @@ namespace MobileGL::MG_Backend::DiligentBackend {
|
||||
const DynamicBackendParameters& GetDynamicParameters() const override;
|
||||
BackendType GetBackendType() const override;
|
||||
|
||||
DiligentRenderer* GetRenderer();
|
||||
|
||||
private:
|
||||
Bool CreateDiligentDevice();
|
||||
|
||||
@@ -56,6 +60,7 @@ namespace MobileGL::MG_Backend::DiligentBackend {
|
||||
::Diligent::IEngineFactoryVk* m_pFactoryVk = nullptr;
|
||||
::Diligent::RefCntAutoPtr<::Diligent::IRenderDevice> m_pDevice;
|
||||
::Diligent::RefCntAutoPtr<::Diligent::IDeviceContext> m_pContext;
|
||||
std::unique_ptr<DiligentRenderer> m_pRenderer;
|
||||
Bool m_initialized = false;
|
||||
};
|
||||
} // namespace MobileGL::MG_Backend::DiligentBackend
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
// 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>
|
||||
|
||||
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;
|
||||
};
|
||||
} // 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();
|
||||
}
|
||||
|
||||
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::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;
|
||||
}
|
||||
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 || !m_pColorRTV) {
|
||||
return;
|
||||
}
|
||||
::Diligent::ITextureView* rtvs[] = {m_pColorRTV};
|
||||
m_pContext->SetRenderTargets(1, rtvs, nullptr, ::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);
|
||||
}
|
||||
|
||||
void DiligentRenderer::DrawTriangle() {
|
||||
if (!m_initialized || !m_pContext || !m_pPSO) {
|
||||
return;
|
||||
}
|
||||
|
||||
::Diligent::ITextureView* rtvs[] = {m_pColorRTV};
|
||||
m_pContext->SetRenderTargets(1, rtvs, nullptr, ::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::ReadPixels(Uint32 x, Uint32 y, Uint32 width, Uint32 height, void* pixels) {
|
||||
if (!m_initialized || !m_pContext || !m_pColorTarget || pixels == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
::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.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{
|
||||
m_pColorTarget, ::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::Present() {
|
||||
// Offscreen renderer: nothing to present yet.
|
||||
if (m_pContext) {
|
||||
m_pContext->Flush();
|
||||
}
|
||||
}
|
||||
} // namespace MobileGL::MG_Backend::DiligentBackend
|
||||
@@ -0,0 +1,66 @@
|
||||
// MobileGL - MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.h
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Includes.h>
|
||||
|
||||
// X11 (pulled in by Includes.h through Vulkan-Headers) defines True/False as
|
||||
// macros, which collide with Diligent's Bool constants in BasicTypes.h.
|
||||
#if defined(True)
|
||||
#undef True
|
||||
#endif
|
||||
#if defined(False)
|
||||
#undef False
|
||||
#endif
|
||||
|
||||
#include <RefCntAutoPtr.hpp>
|
||||
|
||||
namespace Diligent {
|
||||
struct IRenderDevice;
|
||||
struct IDeviceContext;
|
||||
struct ITexture;
|
||||
struct ITextureView;
|
||||
struct IPipelineState;
|
||||
struct IBuffer;
|
||||
}
|
||||
|
||||
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
|
||||
// pixels back. This is the first concrete rendering layer on top of the
|
||||
// Diligent device; it will be expanded into the full MobileGL backend.
|
||||
class DiligentRenderer {
|
||||
public:
|
||||
DiligentRenderer(::Diligent::IRenderDevice* device, ::Diligent::IDeviceContext* context);
|
||||
~DiligentRenderer();
|
||||
|
||||
Bool Initialize(Uint32 width, Uint32 height);
|
||||
void Clear(Float r, Float g, Float b, Float a);
|
||||
void DrawTriangle();
|
||||
void ReadPixels(Uint32 x, Uint32 y, Uint32 width, Uint32 height, void* pixels);
|
||||
void Present();
|
||||
|
||||
::Diligent::IRenderDevice* GetDevice() const { return m_pDevice; }
|
||||
::Diligent::IDeviceContext* GetContext() const { return m_pContext; }
|
||||
|
||||
private:
|
||||
Bool CreateOffscreenTargets();
|
||||
Bool CreatePipeline();
|
||||
Bool CreateVertexBuffer();
|
||||
|
||||
::Diligent::IRenderDevice* m_pDevice = nullptr;
|
||||
::Diligent::IDeviceContext* m_pContext = nullptr;
|
||||
::Diligent::RefCntAutoPtr<::Diligent::ITexture> m_pColorTarget;
|
||||
::Diligent::RefCntAutoPtr<::Diligent::ITextureView> m_pColorRTV;
|
||||
::Diligent::RefCntAutoPtr<::Diligent::IPipelineState> m_pPSO;
|
||||
::Diligent::RefCntAutoPtr<::Diligent::IBuffer> m_pVertexBuffer;
|
||||
Uint32 m_width = 256;
|
||||
Uint32 m_height = 256;
|
||||
Bool m_initialized = false;
|
||||
};
|
||||
} // namespace MobileGL::MG_Backend::DiligentBackend
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <MG_Backend/BackendObject.h>
|
||||
#include <MG_Backend/Diligent/BackendObject_Diligent.h>
|
||||
#include <MG_Backend/Diligent/Renderer/DiligentRenderer.h>
|
||||
#include <MG_Backend/BackendObjects.h>
|
||||
|
||||
using namespace MobileGL;
|
||||
@@ -29,3 +30,28 @@ TEST(DiligentVulkanBackend, CreatesDiligentDeviceAndAdvertisesGL32) {
|
||||
EXPECT_FALSE(backend.GetBackendAPIVersionString().empty());
|
||||
EXPECT_FALSE(backend.GetRendererInfo().BackendName.empty());
|
||||
}
|
||||
|
||||
TEST(DiligentVulkanBackend, ClearsAndDrawsTriangleOffscreen) {
|
||||
DiligentBackend::BackendObject_Diligent backend;
|
||||
backend.Initialize();
|
||||
|
||||
auto* renderer = backend.GetRenderer();
|
||||
if (renderer == nullptr) {
|
||||
GTEST_SKIP() << "No Vulkan adapter available; skipping offscreen rendering test";
|
||||
}
|
||||
|
||||
// Clear to green, then draw a red triangle over the center.
|
||||
renderer->Clear(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
renderer->DrawTriangle();
|
||||
renderer->Present();
|
||||
|
||||
std::uint8_t center[4] = {};
|
||||
renderer->ReadPixels(128, 128, 1, 1, center);
|
||||
EXPECT_GT(center[0], 200) << "center should be red from the triangle";
|
||||
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";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user