diff --git a/assets/CMakeLists.txt b/assets/CMakeLists.txt
index 808dbe5a2..dce8593d6 100644
--- a/assets/CMakeLists.txt
+++ b/assets/CMakeLists.txt
@@ -20,3 +20,4 @@ add_subdirectory(fluid_simulation/shaders)
add_subdirectory(gbuffer/shaders)
add_subdirectory(materials/shaders)
add_subdirectory(oit_demo/shaders)
+add_subdirectory(scene_renderer/shaders)
diff --git a/cmake/ShaderCompile.cmake b/cmake/ShaderCompile.cmake
index 7cacd9136..7ec72734d 100644
--- a/cmake/ShaderCompile.cmake
+++ b/cmake/ShaderCompile.cmake
@@ -56,7 +56,7 @@ function(internal_add_compile_shader_target TARGET_NAME)
add_custom_command(
OUTPUT "${ARG_OUTPUT_FILE}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
- COMMENT "------ Compiling ${ARG_SHADER_STAGE} Shader [${ARG_OUTPUT_FORMAT}] ------"
+ COMMENT "------ Compiling ${ARG_SHADER_STAGE} Shader [${ARG_OUTPUT_FORMAT}] ------${INCLUDE_DIRS}"
MAIN_DEPENDENCY "${ARG_SOURCE}"
DEPENDS ${ARG_INCLUDES}
COMMAND ${CMAKE_COMMAND} -E echo "[${ARG_OUTPUT_FORMAT}] Compiling ${ARG_SHADER_STAGE} ${ARG_SOURCE} to ${ARG_OUTPUT_FILE}"
diff --git a/include/ppx/scene/scene_forward_renderer.h b/include/ppx/scene/scene_forward_renderer.h
new file mode 100644
index 000000000..33ed5f82b
--- /dev/null
+++ b/include/ppx/scene/scene_forward_renderer.h
@@ -0,0 +1,66 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ppx_scene_forward_renderer_h
+#define ppx_scene_forward_renderer_h
+
+#include "ppx/scene/scene_config.h"
+#include "ppx/scene/scene_renderer.h"
+
+namespace ppx {
+namespace scene {
+
+class ForwardRenderer
+ : public scene::Renderer
+{
+private:
+ ForwardRenderer(grfx::Device* pDevice, uint32_t numInFlightFrames);
+
+public:
+ virtual ~ForwardRenderer();
+
+ static ppx::Result Create(grfx::Device* pDevice, uint32_t numFramesInFlight, scene::Renderer** ppRenderer);
+
+private:
+ ppx::Result CreateObjects();
+ void DestroyObjects();
+
+ ppx::Result RenderInternal(scene::RenderOutput* pOutput, grfx::Semaphore* pRenderCompleteSemaphore) override;
+
+private:
+ struct Frame;
+
+ ppx::Result RenderToOutput(
+ ForwardRenderer::Frame& frame,
+ scene::RenderOutput* pOutput,
+ grfx::Semaphore* pRenderCompleteSemaphore);
+
+private:
+ struct Frame
+ {
+ scene::RenderPass depthPrePass;
+ scene::RenderPass shadowPass;
+ scene::RenderPass lightingPass;
+ grfx::CommandBufferPtr renderOutputCmd;
+ grfx::SemaphorePtr timelineSemaphore;
+ uint64_t timelineValue = 0;
+ };
+
+ std::vector mFrames;
+};
+
+} // namespace scene
+} // namespace ppx
+
+#endif // ppx_scene_forward_renderer_h
diff --git a/include/ppx/scene/scene_renderer.h b/include/ppx/scene/scene_renderer.h
new file mode 100644
index 000000000..d3cb4995b
--- /dev/null
+++ b/include/ppx/scene/scene_renderer.h
@@ -0,0 +1,229 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef ppx_scene_renderer_h
+#define ppx_scene_renderer_h
+
+#include "ppx/scene/scene_config.h"
+
+namespace ppx {
+namespace scene {
+
+struct GraphicsPipeline
+{
+ std::string idString = "";
+ grfx::DescriptorSetLayoutPtr descriptorSetLayout = nullptr;
+ grfx::PipelineInterfacePtr pipelineInterface = nullptr;
+ grfx::GraphicsPipelinePtr pipeline = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+struct ComputePipeline
+{
+ std::string idString = "";
+ grfx::DescriptorSetLayoutPtr descriptorSetLayout = nullptr;
+ grfx::PipelineInterfacePtr pipelineInterface = nullptr;
+ grfx::GraphicsPipelinePtr pipeline = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+struct RenderTargetAttachment
+{
+ grfx::ImagePtr image = nullptr;
+ grfx::RenderTargetViewPtr renderTargetView = nullptr;
+ grfx::SampledImageViewPtr sampledImageView = nullptr;
+ grfx::RenderTargetClearValue clearValue = {};
+};
+
+// -------------------------------------------------------------------------------------------------
+
+struct DepthStencilAttachment
+{
+ grfx::ImagePtr image = nullptr;
+ grfx::DepthStencilViewPtr dephtStencilView = nullptr;
+ grfx::SampledImageViewPtr sampledImageView = nullptr;
+ grfx::DepthStencilClearValue clearValue = {};
+};
+
+// -------------------------------------------------------------------------------------------------
+
+struct RenderPass
+{
+ std::string name = "";
+ std::vector renderTargetAttachments = {};
+ scene::DepthStencilAttachment depthStencilAttachment = {};
+ grfx::RenderPassPtr renderPass = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+struct ComputePass
+{
+ const scene::GraphicsPipeline* pPipeline = nullptr;
+ std::vector inputBuffers = {};
+ std::vector inputTextures = {};
+ std::vector outputBuffers = {};
+ std::vector outputTextures = {};
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class RenderOutput
+{
+protected:
+ RenderOutput(
+ scene::Renderer* pRenderer);
+
+public:
+ virtual ~RenderOutput();
+
+ scene::Renderer* GetRenderer() const { return mRenderer; }
+
+ virtual ppx::Result GetRenderTargetImage(
+ grfx::Image** ppImage,
+ grfx::Semaphore* pImageReadySemaphore) = 0;
+
+ virtual bool IsSwapchain() const { return false; }
+
+private:
+ scene::Renderer* mRenderer = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class RenderOutputToImage
+ : public scene::RenderOutput
+{
+protected:
+ RenderOutputToImage(
+ scene::Renderer* pRenderer,
+ grfx::Image* pInitialImage = nullptr);
+
+public:
+ virtual ~RenderOutputToImage();
+
+ static ppx::Result Create(
+ scene::Renderer* pRenderer,
+ grfx::Image* pInitialImage, // Can be NULL
+ scene::RenderOutputToImage** ppRendererOutput);
+
+ static void Destroy(scene::RenderOutputToImage* pRendererOutput);
+
+ virtual ppx::Result GetRenderTargetImage(
+ grfx::Image** ppImage,
+ grfx::Semaphore* pImageReadySemaphore) override;
+
+ void SetImage(grfx::Image* pImage);
+
+private:
+ grfx::Image* mImage = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class RenderOutputToSwapchain
+ : public scene::RenderOutput
+{
+protected:
+ RenderOutputToSwapchain(
+ scene::Renderer* pRenderer,
+ grfx::Swapchain* pInitialSwapchain);
+
+public:
+ virtual ~RenderOutputToSwapchain();
+
+ static ppx::Result Create(
+ scene::Renderer* pRenderer,
+ grfx::Swapchain* pInitialSwapchain, // Can be NULL
+ scene::RenderOutputToSwapchain** ppRendererOutput);
+
+ static void Destroy(scene::RenderOutputToSwapchain* pRendererOutput);
+
+ virtual ppx::Result GetRenderTargetImage(
+ grfx::Image** ppImage,
+ grfx::Semaphore* pImageReadySemaphore) override;
+
+ virtual bool IsSwapchain() const override { return true; }
+
+ void SetSwapchain(grfx::Swapchain* pSwapchain);
+
+private:
+ ppx::Result CreateObject();
+ void DestroyObject();
+
+private:
+ grfx::Swapchain* mSwapchain = nullptr;
+ grfx::FencePtr mFence = nullptr;
+ uint32_t mImageIndex = 0;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class Renderer
+{
+protected:
+ Renderer(
+ grfx::Device* pDevice,
+ uint32_t numInFlightFrames);
+
+public:
+ virtual ~Renderer();
+
+ grfx::Device* GetDevice() const { return mDevice; }
+
+ uint32_t GetNumInFlightFrames() const { return mNumInFlightFrames; }
+ scene::Scene* GetScene() const { return mScene; }
+
+ void SetScene(scene::Scene* pScene);
+
+ ppx::Result Render(
+ scene::RenderOutput* pOutput,
+ grfx::Semaphore* pRenderCompleteSemaphore);
+
+protected:
+ ppx::Result GetRenderOutputRenderPass(
+ grfx::Image* pImage,
+ grfx::RenderPass** ppRenderPass);
+
+private:
+ virtual ppx::Result RenderInternal(
+ scene::RenderOutput* pOutput,
+ grfx::Semaphore* pRenderCompleteSemaphore) = 0;
+
+ // Create render pass with 1 render target using pImage.
+ // OVerride this method in derived renderers to customize output render passes.
+ //
+ virtual ppx::Result CreateOutputRenderPass(
+ grfx::Image* pImage,
+ grfx::RenderPass** ppRenderPass);
+
+protected:
+ grfx::Device* mDevice = nullptr;
+ uint32_t mNumInFlightFrames = 0;
+ uint32_t mNumFramesRendered = 0;
+ uint32_t mCurrentFrameIndex = 0;
+ uint2 mRenderResolution = uint2(0, 0);
+ std::vector mGraphicsPipelines = {};
+ std::vector mComputePipelines = {};
+ std::unordered_map mOutputRenderPasses = {};
+ bool mEnableDepthPrePass = false;
+ scene::Scene* mScene = nullptr;
+};
+
+} // namespace scene
+} // namespace ppx
+
+#endif // ppx_scene_renderer_h
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 00e43ab90..d01f99c53 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -283,19 +283,23 @@ list(
list(
APPEND PPX_SCENE_HEADER_FILES
${INC_DIR}/ppx/scene/scene_config.h
+ ${INC_DIR}/ppx/scene/scene_forward_renderer.h
${INC_DIR}/ppx/scene/scene_material.h
${INC_DIR}/ppx/scene/scene_mesh.h
${INC_DIR}/ppx/scene/scene_node.h
${INC_DIR}/ppx/scene/scene_resource_manager.h
+ ${INC_DIR}/ppx/scene/scene_renderer.h
${INC_DIR}/ppx/scene/scene_scene.h
)
list(
APPEND PPX_SCENE_SOURCE_FILES
+ ${SRC_DIR}/ppx/scene/scene_forward_renderer.cpp
${SRC_DIR}/ppx/scene/scene_material.cpp
${SRC_DIR}/ppx/scene/scene_mesh.cpp
${SRC_DIR}/ppx/scene/scene_node.cpp
${SRC_DIR}/ppx/scene/scene_resource_manager.cpp
+ ${SRC_DIR}/ppx/scene/scene_renderer.cpp
${SRC_DIR}/ppx/scene/scene_scene.cpp
)
diff --git a/src/ppx/scene/scene_forward_renderer.cpp b/src/ppx/scene/scene_forward_renderer.cpp
new file mode 100644
index 000000000..54abafcfb
--- /dev/null
+++ b/src/ppx/scene/scene_forward_renderer.cpp
@@ -0,0 +1,180 @@
+#include "ppx/scene/scene_forward_renderer.h"
+#include "ppx/grfx/grfx_device.h"
+
+namespace ppx {
+namespace scene {
+
+// -------------------------------------------------------------------------------------------------
+// ForwardRenderer
+// -------------------------------------------------------------------------------------------------
+ForwardRenderer::ForwardRenderer(
+ grfx::Device* pDevice,
+ uint32_t numInFlightFrames)
+ : scene::Renderer(pDevice, numInFlightFrames)
+{
+}
+
+ForwardRenderer::~ForwardRenderer()
+{
+ this->DestroyObjects();
+}
+
+ppx::Result ForwardRenderer::CreateObjects()
+{
+ for (uint32_t i = 0; i < GetNumInFlightFrames(); ++i) {
+ Frame frame = {};
+
+ PPX_CHECKED_CALL(GetDevice()->GetGraphicsQueue()->CreateCommandBuffer(&frame.renderOutputCmd));
+
+ /*
+ grfx::SemaphoreCreateInfo semaCreateInfo = {};
+ PPX_CHECKED_CALL(GetDevice()->CreateSemaphore(&semaCreateInfo, &frame.imageAcquiredSemaphore));
+
+ grfx::FenceCreateInfo fenceCreateInfo = {true}; // Create signaled
+ PPX_CHECKED_CALL(GetDevice()->CreateFence(&fenceCreateInfo, &frame.renderCompleteFence));
+ */
+ mFrames.push_back(frame);
+ }
+
+ return ppx::SUCCESS;
+}
+
+void ForwardRenderer::DestroyObjects()
+{
+ for (auto& frame : mFrames) {
+ GetDevice()->GetGraphicsQueue()->DestroyCommandBuffer(frame.renderOutputCmd);
+ // GetDevice()->DestroySemaphore(frame.imageAcquiredSemaphore);
+ // GetDevice()->DestroyFence(frame.renderCompleteFence);
+ }
+}
+
+ppx::Result ForwardRenderer::Create(grfx::Device* pDevice, uint32_t numInFlightFrames, scene::Renderer** ppRenderer)
+{
+ if (IsNull(ppRenderer)) {
+ return ppx::ERROR_UNEXPECTED_NULL_ARGUMENT;
+ }
+
+ scene::ForwardRenderer* pRenderer = new scene::ForwardRenderer(pDevice, numInFlightFrames);
+ if (IsNull(pRenderer)) {
+ return ppx::ERROR_ALLOCATION_FAILED;
+ }
+
+ auto ppxres = pRenderer->CreateObjects();
+ if (Failed(ppxres)) {
+ delete pRenderer;
+ return ppxres;
+ }
+
+ *ppRenderer = pRenderer;
+
+ return ppx::SUCCESS;
+}
+
+ppx::Result ForwardRenderer::RenderToOutput(
+ ForwardRenderer::Frame& frame,
+ scene::RenderOutput* pOutput,
+ grfx::Semaphore* pRenderCompleteSemaphore)
+{
+ // Get output render target image
+ grfx::Image* pOutputImage = nullptr;
+ //
+ // auto ppxres = pOutput->GetRenderTargetImage(&pOutputImage, frame.imageAcquiredSemaphore.Get());
+ // if (Failed(ppxres)) {
+ // return ppxres;
+ //}
+
+ // Get render pass for render output render target image
+ grfx::RenderPass* pOutputRenderPass = nullptr;
+ //
+ auto ppxres = GetRenderOutputRenderPass(
+ pOutputImage,
+ &pOutputRenderPass);
+ if (Failed(ppxres)) {
+ return ppxres;
+ }
+
+ // Output render command buffer
+ {
+ auto& cmd = frame.renderOutputCmd;
+
+ // Begin command buffer
+ ppxres = cmd->Begin();
+ if (Failed(ppxres)) {
+ return ppxres;
+ }
+
+ // Transition swapchain image if needed
+ if (pOutput->IsSwapchain()) {
+ cmd->TransitionImageLayout(
+ pOutputImage,
+ PPX_ALL_SUBRESOURCES,
+ grfx::RESOURCE_STATE_PRESENT,
+ grfx::RESOURCE_STATE_RENDER_TARGET);
+ }
+
+ grfx::RenderPassBeginInfo beginInfo = {};
+ beginInfo.pRenderPass = pOutputRenderPass;
+ beginInfo.renderArea = pOutputRenderPass->GetRenderArea();
+
+ cmd->BeginRenderPass(&beginInfo);
+ {
+ cmd->ClearRenderTarget(pOutputImage, {1, 0, 0, 1});
+ }
+ cmd->EndRenderPass();
+
+ // Transition swapchain image if needed
+ if (pOutput->IsSwapchain()) {
+ cmd->TransitionImageLayout(
+ pOutputImage,
+ PPX_ALL_SUBRESOURCES,
+ grfx::RESOURCE_STATE_RENDER_TARGET,
+ grfx::RESOURCE_STATE_PRESENT);
+ }
+
+ // End command buffer
+ ppxres = cmd->End();
+ if (Failed(ppxres)) {
+ return ppxres;
+ }
+
+ // Submit output render command buffer
+ {
+ grfx::SubmitInfo submitInfo = {};
+ submitInfo.commandBufferCount = 1;
+ submitInfo.ppCommandBuffers = &cmd;
+ // submitInfo.waitSemaphoreCount = 1;
+ // submitInfo.ppWaitSemaphores = &frame.imageAcquiredSemaphore;
+ // submitInfo.signalSemaphoreCount = 1;
+ // submitInfo.ppSignalSemaphores = &pRenderCompleteSemaphore;
+ // submitInfo.pFence = frame.renderCompleteFence;
+
+ PPX_CHECKED_CALL(GetDevice()->GetGraphicsQueue()->Submit(&submitInfo));
+ }
+ }
+
+ return ppx::SUCCESS;
+}
+
+ppx::Result ForwardRenderer::RenderInternal(
+ scene::RenderOutput* pOutput,
+ grfx::Semaphore* pRenderCompleteSemaphore)
+{
+ auto& frame = mFrames[mCurrentFrameIndex];
+
+ // Wait for and reset render complete fence
+ // PPX_CHECKED_CALL(frame.renderCompleteFence->WaitAndReset());
+
+ // Render to output
+ auto ppxres = RenderToOutput(
+ frame,
+ pOutput,
+ pRenderCompleteSemaphore);
+ if (Failed(ppxres)) {
+ return ppxres;
+ }
+
+ return ppx::SUCCESS;
+}
+
+} // namespace scene
+} // namespace ppx
diff --git a/src/ppx/scene/scene_renderer.cpp b/src/ppx/scene/scene_renderer.cpp
new file mode 100644
index 000000000..69aa63379
--- /dev/null
+++ b/src/ppx/scene/scene_renderer.cpp
@@ -0,0 +1,292 @@
+#include "ppx/scene/scene_renderer.h"
+#include "ppx/grfx/grfx_device.h"
+#include "ppx/grfx/grfx_swapchain.h"
+
+namespace ppx {
+namespace scene {
+
+// -------------------------------------------------------------------------------------------------
+// RendererOutput
+// -------------------------------------------------------------------------------------------------
+RenderOutput::RenderOutput(
+ scene::Renderer* pRenderer)
+ : mRenderer(pRenderer)
+{
+}
+
+RenderOutput::~RenderOutput()
+{
+}
+
+// -------------------------------------------------------------------------------------------------
+// RendererOutputToImage
+// -------------------------------------------------------------------------------------------------
+RenderOutputToImage::RenderOutputToImage(
+ scene::Renderer* pRenderer,
+ grfx::Image* pInitialImage)
+ : scene::RenderOutput(pRenderer),
+ mImage(pInitialImage)
+{
+}
+
+RenderOutputToImage::~RenderOutputToImage()
+{
+}
+
+ppx::Result RenderOutputToImage::Create(
+ scene::Renderer* pRenderer,
+ grfx::Image* pInitialImage,
+ scene::RenderOutputToImage** ppRendererOutput)
+{
+ if (IsNull(pRenderer) || IsNull(ppRendererOutput)) {
+ return ppx::ERROR_UNEXPECTED_NULL_ARGUMENT;
+ }
+
+ auto pObject = new scene::RenderOutputToImage(pRenderer, pInitialImage);
+ if (IsNull(pObject)) {
+ return ppx::ERROR_ALLOCATION_FAILED;
+ }
+
+ *ppRendererOutput = pObject;
+
+ return ppx::SUCCESS;
+}
+
+void RenderOutputToImage::Destroy(scene::RenderOutputToImage* pRendererOutput)
+{
+ if (IsNull(pRendererOutput)) {
+ return;
+ }
+
+ delete pRendererOutput;
+}
+
+ppx::Result RenderOutputToImage::GetRenderTargetImage(
+ grfx::Image** ppImage,
+ grfx::Semaphore* pImageReadySemaphore)
+{
+ (void)pImageReadySemaphore;
+
+ if (IsNull(ppImage)) {
+ return ppx::ERROR_UNEXPECTED_NULL_ARGUMENT;
+ }
+
+ *ppImage = mImage;
+
+ return ppx::SUCCESS;
+}
+
+void RenderOutputToImage::SetImage(grfx::Image* pImage)
+{
+ mImage = pImage;
+}
+
+// -------------------------------------------------------------------------------------------------
+// RendererOutputToSwapchain
+// -------------------------------------------------------------------------------------------------
+RenderOutputToSwapchain::RenderOutputToSwapchain(
+ scene::Renderer* pRenderer,
+ grfx::Swapchain* pInitialSwapchain)
+ : scene::RenderOutput(pRenderer),
+ mSwapchain(pInitialSwapchain)
+{
+}
+
+RenderOutputToSwapchain::~RenderOutputToSwapchain()
+{
+}
+
+ppx::Result RenderOutputToSwapchain::CreateObject()
+{
+ // Create fence for image acquisition
+ {
+ grfx::FenceCreateInfo createInfo = {};
+
+ auto ppxres = GetRenderer()->GetDevice()->CreateFence(
+ &createInfo,
+ &mFence);
+ if (Failed(ppxres)) {
+ return ppxres;
+ }
+ }
+
+ return ppx::SUCCESS;
+}
+
+void RenderOutputToSwapchain::DestroyObject()
+{
+ if (mFence) {
+ GetRenderer()->GetDevice()->DestroyFence(mFence);
+ mFence.Reset();
+ }
+}
+
+ppx::Result RenderOutputToSwapchain::Create(
+ scene::Renderer* pRenderer,
+ grfx::Swapchain* pInitialSwapchain,
+ scene::RenderOutputToSwapchain** ppRendererOutput)
+{
+ if (IsNull(pRenderer) || IsNull(ppRendererOutput)) {
+ return ppx::ERROR_UNEXPECTED_NULL_ARGUMENT;
+ }
+
+ auto pRendererOutput = new scene::RenderOutputToSwapchain(
+ pRenderer,
+ pInitialSwapchain);
+ if (IsNull(pRendererOutput)) {
+ return ppx::ERROR_ALLOCATION_FAILED;
+ }
+
+ auto ppxres = pRendererOutput->CreateObject();
+ if (Failed(ppxres)) {
+ delete pRendererOutput;
+ return ppxres;
+ }
+
+ *ppRendererOutput = pRendererOutput;
+
+ return ppx::SUCCESS;
+}
+
+void RenderOutputToSwapchain::Destroy(scene::RenderOutputToSwapchain* pRendererOutput)
+{
+ if (IsNull(pRendererOutput)) {
+ return;
+ }
+
+ pRendererOutput->DestroyObject();
+
+ delete pRendererOutput;
+}
+
+ppx::Result RenderOutputToSwapchain::GetRenderTargetImage(
+ grfx::Image** ppImage,
+ grfx::Semaphore* pImageReadySemaphore)
+{
+ if (IsNull(ppImage)) {
+ return ppx::ERROR_UNEXPECTED_NULL_ARGUMENT;
+ }
+
+ auto ppxres = mSwapchain->AcquireNextImage(
+ UINT64_MAX,
+ pImageReadySemaphore,
+ mFence,
+ &mImageIndex);
+ if (Failed(ppxres)) {
+ return ppxres;
+ }
+
+ ppxres = mFence->WaitAndReset();
+ if (Failed(ppxres)) {
+ return ppxres;
+ }
+
+ *ppImage = mSwapchain->GetColorImage(mImageIndex).Get();
+
+ return ppx::SUCCESS;
+}
+
+void RenderOutputToSwapchain::SetSwapchain(grfx::Swapchain* pSwapchain)
+{
+ mSwapchain = pSwapchain;
+}
+
+// -------------------------------------------------------------------------------------------------
+// Renderer
+// -------------------------------------------------------------------------------------------------
+Renderer::Renderer(
+ grfx::Device* pDevice,
+ uint32_t numInFlightFrames)
+ : mDevice(pDevice),
+ mNumInFlightFrames(numInFlightFrames)
+{
+}
+
+Renderer::~Renderer()
+{
+}
+
+ppx::Result Renderer::GetRenderOutputRenderPass(
+ grfx::Image* pImage,
+ grfx::RenderPass** ppRenderPass)
+{
+ if (IsNull(pImage) || IsNull(ppRenderPass)) {
+ return ppx::ERROR_UNEXPECTED_NULL_ARGUMENT;
+ }
+
+ grfx::RenderPassPtr renderPass = nullptr;
+
+ // Find a render pass for pImage, if there isn't one create it.
+ auto it = mOutputRenderPasses.find(pImage);
+ if (it != mOutputRenderPasses.end()) {
+ renderPass = (*it).second;
+ }
+ else {
+ auto ppxres = CreateOutputRenderPass(
+ pImage,
+ &renderPass);
+ if (Failed(ppxres)) {
+ return ppxres;
+ }
+
+ mOutputRenderPasses[pImage] = renderPass;
+ }
+
+ *ppRenderPass = renderPass.Get();
+
+ return ppx::SUCCESS;
+}
+
+void Renderer::SetScene(scene::Scene* pScene)
+{
+ mScene = pScene;
+}
+
+ppx::Result Renderer::Render(
+ scene::RenderOutput* pOutput,
+ grfx::Semaphore* pRenderCompleteSemaphore)
+{
+ if (IsNull(pOutput)) {
+ return ppx::ERROR_UNEXPECTED_NULL_ARGUMENT;
+ }
+
+ auto ppxres = this->RenderInternal(
+ pOutput,
+ pRenderCompleteSemaphore);
+ if (Failed(ppxres)) {
+ return ppxres;
+ }
+
+ return ppx::SUCCESS;
+}
+
+ppx::Result Renderer::CreateOutputRenderPass(
+ grfx::Image* pImage,
+ grfx::RenderPass** ppRenderPass)
+{
+ if (IsNull(pImage) || IsNull(ppRenderPass)) {
+ return ppx::ERROR_UNEXPECTED_NULL_ARGUMENT;
+ }
+
+ grfx::RenderPassCreateInfo3 createInfo = {};
+ createInfo.width = pImage->GetWidth();
+ createInfo.height = pImage->GetHeight();
+ createInfo.renderTargetCount = 1;
+ createInfo.pRenderTargetImages[0] = pImage;
+ createInfo.renderTargetClearValues[0] = grfx::RenderTargetClearValue{0, 0, 0, 0};
+ createInfo.renderTargetLoadOps[0] = grfx::ATTACHMENT_LOAD_OP_LOAD;
+ createInfo.renderTargetStoreOps[0] = grfx::ATTACHMENT_STORE_OP_STORE;
+
+ grfx::RenderPassPtr renderPass = nullptr;
+ auto ppxres = GetDevice()->CreateRenderPass(&createInfo, &renderPass);
+ if (Failed(ppxres)) {
+ return ppxres;
+ }
+
+ *ppRenderPass = renderPass.Get();
+
+ return ppx::SUCCESS;
+}
+
+} // namespace scene
+} // namespace ppx