nCine::RHI::RSX::RsxDevice class

Pipeline-state and draw-call facade of the RSX backend (aliased as RHI::Device).

Exposes the OpenGL device's surface (blending, depth, cull, scissor, viewport, clear and the draw calls) so the backend-neutral render pipeline drives it unchanged, and owns the whole RSX session behind it: the command FIFO, the negotiated video mode, the display buffers and the shared depth buffer.

Three properties of the hardware shape everything below.

The RSX is immediate-mode, which is what makes this simpler than the sceGxm backend.** There is no scene to begin and end and no tile buffer whose contents start undefined: a render target is a set of addresses in a state register, rsxSetSurface() changes it, and rsxClearSurface() is a real hardware clear rather than a full-screen quad standing in for one. A target can be re-entered mid-frame without discarding what an earlier pass drew, so the pipeline's viewport chain needs no special handling at all.

The output resolution is negotiated with the display.** Unlike every other console backend here, the panel is not fixed: CreateSwapchain() walks a preference list through videoGetResolutionAvailability() and takes the first mode the attached display accepts, so GetDisplayWidth() and GetDisplayHeight() are only known after the session is up. The logical (game) resolution remains a render-target size driven separately by the render pipeline.

The display scans out top-down.** The engine renders in the OpenGL convention, and this backend replays it faithfully: every viewport is programmed so clip -Y lands on row 0 and every surface - the screen and each off-screen render target - is stored bottom-up exactly like OpenGL. That is what keeps a texture's V axis and its viewport's Y axis pointing the same way, so an off-screen round trip does not flip the image (the property that makes the scene composite and the directly drawn HUD agree). The scan-out then needs the one correction that convention implies: "screen" (no render target bound) is an intermediate surface, and PresentFrame() flips it into the display buffer with a built-in shader - the same single OpenGL-to-native flip the sceGxm and Direct3D 11 backends do, which also scales the logical resolution up to the panel for free.

Public types

struct ScissorState
struct BlendingState
struct DepthTestState
struct CullFaceState

Public static variables

static std::uint32_t MaxBatchSize constexpr
Largest batch the GetBatchedCornerStream() covers.

Public static functions

static void SetBlendingEnabled(bool enabled)
static void SetBlendingFactors(nCine::BlendingFactor srcRgb, nCine::BlendingFactor dstRgb, nCine::BlendingFactor srcAlpha, nCine::BlendingFactor dstAlpha)
static auto GetBlendingState() -> BlendingState
static void SetBlendingState(const BlendingState& state)
static void SetDepthTestEnabled(bool enabled)
static void SetDepthMaskEnabled(bool enabled)
static auto GetDepthTestState() -> DepthTestState
static void SetDepthTestState(const DepthTestState& state)
static void SetCullFaceEnabled(bool enabled)
static auto GetCullFaceState() -> CullFaceState
static void SetCullFaceState(const CullFaceState& state)
static auto GetScissorState() -> ScissorState
static void SetScissorState(const ScissorState& state)
static void SetScissor(const Recti& rect)
static void SetScissorTestEnabled(bool enabled)
static auto GetViewport() -> Recti
static void SetViewport(const Recti& rect)
static void InitViewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height)
static auto GetClearColor() -> Colorf
static void SetClearColor(const Colorf& color)
static void Clear(ClearFlags flags)
static void DrawArrays(PrimitiveType primitive, std::int32_t firstVertex, std::int32_t numVertices)
static void DrawArraysInstanced(PrimitiveType primitive, std::int32_t firstVertex, std::int32_t numVertices, std::int32_t numInstances)
static void DrawElements(PrimitiveType primitive, std::uint32_t numIndices, IndexFormat indexFormat, std::uintptr_t indexOffset, std::int32_t baseVertex)
static void DrawElements(PrimitiveType primitive, std::uint32_t numIndices, std::uintptr_t indexOffset, std::int32_t baseVertex)
static void DrawElementsInstanced(PrimitiveType primitive, std::uint32_t numIndices, IndexFormat indexFormat, std::uintptr_t indexOffset, std::int32_t numInstances, std::int32_t baseVertex)
static void DrawElementsInstanced(PrimitiveType primitive, std::uint32_t numIndices, std::uintptr_t indexOffset, std::int32_t numInstances, std::int32_t baseVertex)
static auto InsertFence() -> FenceHandle
static void DeleteFence(FenceHandle& fence)
static auto ClientWaitFence(FenceHandle fence, std::uint64_t timeoutNs) -> bool
static void SetupInitialState()
static void BindProgram(RsxShaderProgram* program)
Records the currently bound shader program.
static auto CurrentProgram() -> RsxShaderProgram*
Returns the currently bound shader program.
static void BindTexture(std::uint32_t unit, const RsxTexture* texture)
Records the texture bound to a texture unit.
static void UnbindTexture(const RsxTexture* texture)
Clears a texture from every unit it is bound to (called from ~RsxTexture to avoid a dangling pointer).
static auto GetBoundTexture(std::uint32_t unit) -> const RsxTexture*
Returns the texture bound to a texture unit.
static void BindUniformRange(std::uint32_t index, const std::uint8_t* data, std::uint32_t size)
Records the host data range bound to a uniform binding point.
static void GetUniformRange(std::uint32_t index, const std::uint8_t*& data, std::uint32_t& size)
Returns the host data range bound to a uniform binding point (read by the draw path).
static void UnbindRenderTarget(const RsxRenderTarget* renderTarget)
Clears a render target from the device if it is the current one (called from ~RsxRenderTarget).
static void SetRenderTarget(RsxRenderTarget* renderTarget)
Records the current draw render target (its bound colour attachment receives the pixels).
static void OnProgramDestroyed(const RsxShaderProgram* program)
Clears a destroyed program from the device's current-program tracking (called from ~RsxShaderProgram).
static auto GetContext() -> gcmContextData*
Returns the command context, or nullptr before CreateSwapchain().
static void Finish()
Blocks until the GPU has consumed everything submitted so far.
static void RetireBlock(RsxVram::Block& block)
Holds block until the frame that may still reference it is done, and invalidates it.
static auto GetFrameCounter() -> std::uint32_t
Monotonic count of presented frames.
static auto GetQuadCornerStream() -> const void*
Returns the static vertex stream feeding aQuadCorner for the 4-vertex sprite strip.
static auto GetBatchedCornerStream() -> const void*
Returns the static vertex stream feeding aQuadCorner + aInstanceIndex for batched sprites.
static auto GetQuadCornerStreamOffset() -> std::uint32_t
Offset of the quad corner stream, as rsxBindVertexArrayAttrib() takes it.
static auto GetBatchedCornerStreamOffset() -> std::uint32_t
Offset of the batched corner stream.
static auto CreateSwapchain(void* windowHandle, std::int32_t width, std::int32_t height, bool vsync) -> bool
Brings up the whole RSX session.
static void DestroySwapchain()
Tears the session down (waits for the GPU, then releases everything in creation order).
static void ResizeSwapchain(std::int32_t width, std::int32_t height)
No-op: the display mode is negotiated once (the logical resolution is a render-target size).
static void PresentFrame()
Flips the intermediate screen surface into the next display buffer and queues it for scan-out.
static auto CreateSecondarySwapchain(void* windowHandle, std::int32_t width, std::int32_t height) -> void*
Not supported on this platform (returns nullptr).
static void DestroySecondarySwapchain(void* handle)
Not supported on this platform.
static void ResizeSecondarySwapchain(void* handle, std::int32_t width, std::int32_t height)
Not supported on this platform.
static void BeginSecondaryFrame(void* handle, bool clear)
Not supported on this platform.
static void EndSecondaryFrame()
Not supported on this platform.
static void PresentSecondaryFrame(void* handle)
Not supported on this platform.
static auto GetDisplayWidth() -> std::int32_t
Width in pixels of the negotiated display mode (0 before CreateSwapchain()).
static auto GetDisplayHeight() -> std::int32_t
Height in pixels of the negotiated display mode.
static auto GetMaxTextureDimension() -> std::int32_t constexpr
Returns the largest supported 2D texture dimension.
static auto GetUniformBufferOffsetAlignment() -> std::int32_t constexpr
Byte alignment the engine suballocates bound uniform ranges with (one std140 vec4).
static auto GetMaxUniformBlockSize() -> std::int32_t constexpr
Byte budget of one uniform block, which here is the vertex constant-register file.

Constructors, destructors, conversion operators

RsxDevice() deleted
~RsxDevice() deleted

Function documentation

static void nCine::RHI::RSX::RsxDevice::Finish()

Blocks until the GPU has consumed everything submitted so far.

Used by the two operations that read what the GPU wrote - a render target's readback and the teardown - and by nothing on a frame path.

static void nCine::RHI::RSX::RsxDevice::RetireBlock(RsxVram::Block& block)

Holds block until the frame that may still reference it is done, and invalidates it.

The command FIFO runs behind the CPU, so memory freed the moment a resource is destroyed may still be read by commands already queued. Retired blocks are released after the next present.

static const void* nCine::RHI::RSX::RsxDevice::GetQuadCornerStream()

Returns the static vertex stream feeding aQuadCorner for the 4-vertex sprite strip.

The corners are in the order of the single-quad TRIANGLE_STRIP draw - {(1,0), (1,1), (0,0), (0,1)} - which is what the Cg emitter's rewrite of the engine's gl_VertexID corner formula expects (see ShaderCompiler::VertexIdRewrite). Two floats per vertex.

static const void* nCine::RHI::RSX::RsxDevice::GetBatchedCornerStream()

Returns the static vertex stream feeding aQuadCorner + aInstanceIndex for batched sprites.

Six vertices per sprite (two triangles) carrying the batched corner order {(1,1), (0,1), (0,0), (0,0), (1,0), (1,1)} and the sprite's index within the batch, so a batched draw of 6 * n vertices reproduces the gl_VertexID / 6 instance lookup and the corner formula the emitter rewrote. Three floats per vertex; covers MaxBatchSize sprites.

static bool nCine::RHI::RSX::RsxDevice::CreateSwapchain(void* windowHandle, std::int32_t width, std::int32_t height, bool vsync)

Brings up the whole RSX session.

Parameters
windowHandle Ignored (the console has one display, so there is no window to attach to); accepted only to keep the uniform swap-chain contract of the other backends
width Ignored, the mode is negotiated with the attached display
height Ignored
vsync Whether the present waits for vertical blank
Returns true if the FIFO, the video mode, the surfaces and the built-in shaders all came up

static std::int32_t nCine::RHI::RSX::RsxDevice::GetMaxTextureDimension() constexpr

Returns the largest supported 2D texture dimension.

4096 is the NV47's limit; it only feeds the tileset chunking in ContentResolver and the texture-load assert, both of which the console's content stays well below.

static std::int32_t nCine::RHI::RSX::RsxDevice::GetMaxUniformBlockSize() constexpr

Byte budget of one uniform block, which here is the vertex constant-register file.

The RSX gives a vertex program constant registers and nothing else to read uniforms from - there is no uniform buffer to bind - and the vp40 profile allows a program 544 of them. The engine's batcher divides the figure published here by the instance stride to decide how large a batch may be, so it is set conservatively rather than at the ceiling; MaxBatchSize bounds the result again in any case. What matters is only that it never lets the batcher build a batch whose later elements the shader cannot address.

Variable documentation

static std::uint32_t nCine::RHI::RSX::RsxDevice::MaxBatchSize constexpr

Largest batch the GetBatchedCornerStream() covers.

Far smaller than the sceGxm backend's 682, and for a hardware reason rather than a policy one: a batched instance array reaches the vertex program through its constant registers, and the vp40 profile allows a program 544 of them in total. The compiler's own accounting is not a simple product of that and the element width - measured against the batched shaders, they stop compiling somewhere above 40 instances - so 32 is the value they all accept with margin.