Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,920 changes: 913 additions & 1,007 deletions package-lock.json

Large diffs are not rendered by default.

115 changes: 114 additions & 1 deletion src/renderer/content_renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
#include <xr/device.hpp>
#include <xr/session.hpp>

#include <common/command_buffers/details/buffer.hpp>
#include <common/command_buffers/details/states.hpp>
#include <common/command_buffers/webgl_constants.hpp>

#include "./content_renderer.hpp"
#include "./render_api.hpp"

Expand Down Expand Up @@ -162,6 +166,65 @@ namespace renderer
commandBuffersOnOffscreenPass.push_back(req);
}

RenderPassType TrContentRenderer::determineRenderPassType(TrCommandBufferBase *req) const
{
if (req == nullptr) [[unlikely]]
{
return RenderPassType::kOpaque;
}

auto commandType = req->type;

// Check if this is a framebuffer bind operation
if (commandType == COMMAND_BUFFER_BIND_FRAMEBUFFER_REQ)
{
auto bindFbReq = dynamic_cast<const commandbuffers::BindFramebufferCommandBufferRequest *>(req);
if (bindFbReq != nullptr)
{
// If binding to a non-default framebuffer, route to offscreen pass
if (!bindFbReq->isBindToDefault())
{
return RenderPassType::kOffscreen;
}
}
}

// Check if the current framebuffer binding is not the default render target
if (currentBoundFramebuffer_.has_value())
{
auto glContext = getContextGL();
if (glContext != nullptr)
{
GLuint currentDefault = glContext->currentDefaultRenderTarget();
if (currentBoundFramebuffer_.value() != currentDefault && currentBoundFramebuffer_.value() != 0)
{
// Currently bound to a non-default framebuffer, route to offscreen
return RenderPassType::kOffscreen;
}
}
}

// If blending is enabled and this is a draw call, route to transparent pass
// Only check for actual draw commands, not all framebuffer-dependent commands
if (isBlendingEnabled_)
{
switch (commandType)
{
case COMMAND_BUFFER_DRAW_ARRAYS_REQ:
case COMMAND_BUFFER_DRAW_ELEMENTS_REQ:
case COMMAND_BUFFER_DRAW_ARRAYS_INSTANCED_REQ:
case COMMAND_BUFFER_DRAW_ELEMENTS_INSTANCED_REQ:
case COMMAND_BUFFER_DRAW_RANGE_ELEMENTS_REQ:
return RenderPassType::kTransparent;
default:
break;
}
}

// Default to opaque pass
return RenderPassType::kOpaque;
}

// The `req` argument is a pointer to `TrCommandBufferBase` in the heap, it will be stored in the corresponding queues
// such as `defaultCommandBufferRequests` or `stereoFramesList`, otherwise it will be deleted in this function.
void TrContentRenderer::onCommandBufferRequestReceived(TrCommandBufferBase *req)
Expand Down Expand Up @@ -340,17 +403,44 @@ namespace renderer

void TrContentRenderer::onTransparentsRenderPass(chrono::time_point<chrono::high_resolution_clock> time)
{
// TODO(yorkie): implement the transparents render pass.
// Execute command buffers that have been routed to the transparent render pass.
// These are typically draw calls that have blending enabled.
auto *transparentPass = renderPassCollection_.getTransparentPass();
if (transparentPass != nullptr && transparentPass->hasCommandBuffers())
{
// Note: We use kXRFrame pass type for transparent rendering because it indicates
// a regular rendering frame (as opposed to initialization or offscreen). The
// transparent pass executes draw calls with blending, which are still part of
// the main XR/rendering frame.
currentPass = ExecutingPassType::kXRFrame;

transparentPass->begin();
{
auto &commandBuffers = transparentPass->getCommandBuffers();
constellation->renderer->executeCommandBuffers(commandBuffers,
this,
ExecutingPassType::kXRFrame);
}
transparentPass->end();
transparentPass->clearCommandBuffers();
}
}

void TrContentRenderer::onOffscreenRenderPass()
{
currentPass = ExecutingPassType::kOffscreenPass;

// Get the offscreen render pass from the collection
auto *offscreenPass = renderPassCollection_.getOffscreenPass();

// Execute command buffers from both the legacy storage and the new render pass collection
// This maintains backward compatibility while transitioning to the new system
if (commandBuffersOnOffscreenPass.size() > 0)
{
assert(glContextOnOffscreenPass.has_value() &&
"The offscreen pass context is not initialized, please call `initializeGraphicsContextsOnce()` first.");

offscreenPass->begin();
glContextOnOffscreenPass->restore();
constellation->renderer->executeCommandBuffers(commandBuffersOnOffscreenPass,
this,
Expand All @@ -363,7 +453,23 @@ namespace renderer

commandBuffersOnOffscreenPass.clear();
glContextOnOffscreenPass = nullopt;
offscreenPass->end();
}

// Also check the render pass collection for any additional offscreen commands
if (offscreenPass->hasCommandBuffers())
{
offscreenPass->begin();
{
auto &commandBuffers = offscreenPass->getCommandBuffers();
constellation->renderer->executeCommandBuffers(commandBuffers,
this,
ExecutingPassType::kOffscreenPass);
}
offscreenPass->end();
offscreenPass->clearCommandBuffers();
}

currentPass = ExecutingPassType::kDefaultFrame;
}

Expand All @@ -372,6 +478,13 @@ namespace renderer
frameStartTime = chrono::system_clock::now();
currentPass = ExecutingPassType::kDefaultFrame;

// Reset the render pass collection for a new frame
renderPassCollection_.resetForNewFrame();

// Reset blending and framebuffer state tracking
isBlendingEnabled_ = false;
currentBoundFramebuffer_ = std::nullopt;

// Update the pending stereo frames count for each WebXR session if the WebXR device is enabled.
if (xrDevice->enabled()) [[likely]]
{
Expand Down
81 changes: 81 additions & 0 deletions src/renderer/content_renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <runtime/macros.h>
#include <xr/device.hpp>
#include <renderer/render_api.hpp>
#include <renderer/render_pass.hpp>
#include <renderer/gles/context_storage.hpp>

using namespace std;
Expand All @@ -25,6 +26,7 @@ namespace renderer
{
class TrRenderer;
class TrContentRenderer;
class TrRenderPass;

/**
* A scope class for backup GL context, using this class will automatically restore the gl context after the scope:
Expand Down Expand Up @@ -122,6 +124,66 @@ namespace renderer
void resetOffscreenPassGLContext(std::optional<GLuint> framebuffer);
void scheduleCommandBufferAtOffscreenPass(TrCommandBufferBase *req);

/**
* Determine which render pass a command buffer should be routed to.
*
* @param req The command buffer request to evaluate.
* @returns The appropriate render pass type for this command buffer.
*/
RenderPassType determineRenderPassType(TrCommandBufferBase *req) const;

/**
* Get the render pass collection for this content renderer.
*
* @returns A reference to the render pass collection.
*/
inline TrRenderPassCollection &getRenderPassCollection()
{
return renderPassCollection_;
}

/**
* Update the blending state.
* Call this when the GL_BLEND capability is enabled/disabled.
*
* @param enabled Whether blending is enabled.
*/
inline void setBlendingEnabled(bool enabled)
{
isBlendingEnabled_ = enabled;
}

/**
* Check if blending is currently enabled.
*
* @returns `true` if blending is enabled, `false` otherwise.
*/
inline bool isBlendingEnabled() const
{
return isBlendingEnabled_;
}

/**
* Update the current bound framebuffer.
* Call this when a framebuffer is bound.
*
* @param framebuffer The framebuffer ID, or nullopt for default.
*/
inline void setCurrentBoundFramebuffer(std::optional<GLuint> framebuffer)
{
currentBoundFramebuffer_ = framebuffer;
}

/**
* Get the current bound framebuffer.
*
* @returns The current framebuffer ID, or nullopt if using default.
*/
inline std::optional<GLuint> getCurrentBoundFramebuffer() const
{
return currentBoundFramebuffer_;
}

private: // private lifecycle
/**
* The callback function to handle the command buffer request received.
Expand Down Expand Up @@ -208,5 +270,24 @@ namespace renderer

private: // frame rate control
uint32_t targetFrameRate;

private: // render pass management
/**
* The collection of render passes for this content renderer.
* Contains opaque, transparent, and offscreen render passes.
*/
TrRenderPassCollection renderPassCollection_;

/**
* The current framebuffer binding state.
* Used to determine if a command buffer should be routed to offscreen pass.
*/
std::optional<GLuint> currentBoundFramebuffer_;

/**
* Whether blending is currently enabled.
* Used to determine if draw calls should be routed to the transparent pass.
*/
bool isBlendingEnabled_ = false;
};
}
20 changes: 20 additions & 0 deletions src/renderer/render_api_opengles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,16 @@ class RHI_OpenGL : public TrRenderHardwareInterface
app_context->bindFramebuffer(req->target,
req->isBindToDefault() ? nullopt : make_optional<uint32_t>(req->framebuffer),
framebuffer);

// Track the current bound framebuffer for render pass routing
if (req->target == GL_FRAMEBUFFER || req->target == GL_DRAW_FRAMEBUFFER)
{
if (req->isBindToDefault())
reqContentRenderer->setCurrentBoundFramebuffer(std::nullopt);
else
reqContentRenderer->setCurrentBoundFramebuffer(framebuffer);
}

if (TR_UNLIKELY(CheckError(req, reqContentRenderer) != GL_NO_ERROR || options.printsCall))
PrintDebugInfo(req, to_string(framebuffer).c_str(), nullptr, options);
}
Expand Down Expand Up @@ -2512,6 +2522,11 @@ class RHI_OpenGL : public TrRenderHardwareInterface
auto cap = req->cap;
glEnable(cap);
reqContentRenderer->getContextGL()->onCapabilityEnabled(cap, true);

// Track blending state for render pass routing
if (cap == GL_BLEND)
reqContentRenderer->setBlendingEnabled(true);

if (TR_UNLIKELY(CheckError(req, reqContentRenderer) != GL_NO_ERROR || options.printsCall))
PrintDebugInfo(req, nullptr, nullptr, options);
}
Expand All @@ -2520,6 +2535,11 @@ class RHI_OpenGL : public TrRenderHardwareInterface
auto cap = req->cap;
glDisable(cap);
reqContentRenderer->getContextGL()->onCapabilityEnabled(cap, false);

// Track blending state for render pass routing
if (cap == GL_BLEND)
reqContentRenderer->setBlendingEnabled(false);

if (TR_UNLIKELY(CheckError(req, reqContentRenderer) != GL_NO_ERROR || options.printsCall))
PrintDebugInfo(req, nullptr, nullptr, options);
}
Expand Down
Loading