blob: c054b4dbe987da934e7f4830e90d0d99c2e69ba5 [file]
/*
* Copyright 2023 Rive
*/
#pragma once
#include "rive/renderer/render_context.hpp"
#include "rive/gpu_texture_format.hpp"
#ifdef RIVE_CANVAS
#include "rive/renderer/render_canvas.hpp"
#include <memory>
namespace rive::ore
{
class Context;
};
#endif
namespace rive::gpu
{
class Texture;
// This class manages GPU buffers and isues the actual rendering commands from
// RenderContext.
class RenderContextImpl
{
public:
virtual ~RenderContextImpl() {}
const PlatformFeatures& platformFeatures() const
{
return m_platformFeatures;
}
virtual rcp<RenderBuffer> makeRenderBuffer(RenderBufferType,
RenderBufferFlags,
size_t) = 0;
#ifdef WITH_RIVE_TOOLS
// Changes how pipelines are selected and compiled for subsequent flushes,
// returning the previous mode. Backends that don't support runtime
// changes ignore the request (and echo the requested mode back). Testing
// only: lets the golden tests render individual frames through
// ubershaders.
virtual ShaderCompilationMode testingOnly_setShaderCompilationMode(
ShaderCompilationMode mode)
{
return mode;
}
#endif
// Use platform apis to decode the image bytes and creates a texture if
// available. If not available leaving its default implementation will cause
// rive decoders to be used instead
virtual rcp<Texture> platformDecodeImageTexture(
Span<const uint8_t> encodedBytes)
{
return nullptr;
};
// this is called in the case of the default Bitmap class being used to
// decode images so that it can be converted into a backend specific image.
// For compressed `format`s, `blockWidth`/`blockHeight` give the format's
// block footprint (e.g. 4x4 for BC7 and ASTC 4x4) and `srgb` selects the
// sRGB variant of the format. For rgba32 these are ignored.
//
// `mipLevelCount` is the number of stored mip levels in `imageData`,
// packed largest-first with no inter-level padding. When
// `generateRemainingMips` is true (PNG/JPEG path), only mip 0 bytes are
// expected in `imageData` and the backend fills the remaining levels
// via GPU blits. When false (KTX2 path), the caller has supplied the
// full chain and the backend uploads it verbatim.
virtual rcp<Texture> makeImageTexture(
uint32_t width,
uint32_t height,
uint32_t mipLevelCount,
GPUTextureFormat format,
const uint8_t imageData[],
uint8_t blockWidth = 1,
uint8_t blockHeight = 1,
bool srgb = false,
bool generateRemainingMips = false) = 0;
#ifdef RIVE_CANVAS
// Allocates a canvas's texture and render target on this device, once,
// before its first use here. A backend with no canvas support leaves the
// canvas unbacked, which is how makeRenderCanvas reports it made none.
virtual void ensureCanvasBacking(RenderCanvas*) {}
// A canvas nothing has allocated for yet. The recording only needs the
// image identity it refers to, so this touches no device and whichever
// context replays owns the pixels.
rcp<RenderCanvas> makeDeferredRenderCanvas(uint32_t width, uint32_t height)
{
return make_rcp<RenderCanvas>(width, height);
}
// Creates a RenderCanvas: a GPU texture usable as both a render target
// and a render image. Returns nullptr if not supported by this backend.
rcp<RenderCanvas> makeRenderCanvas(uint32_t width, uint32_t height)
{
rcp<RenderCanvas> canvas = makeDeferredRenderCanvas(width, height);
ensureCanvasBacking(canvas.get());
return canvas->isBacked() ? canvas : nullptr;
}
// If canvas is enabled then the backend Impl MUST implement this.
virtual std::unique_ptr<rive::ore::Context> makeOreContext() = 0;
#endif
// Resize GPU buffers. These methods cannot fail, and must allocate the
// exact size requested.
//
// RenderContext takes care to minimize how often these methods are called,
// while also growing and shrinking the memory footprint to fit current
// usage.
//
// 'elementSizeInBytes' represents the size of one array element when the
// shader accesses this buffer as a storage buffer.
virtual void resizeFlushUniformBuffer(size_t sizeInBytes) = 0;
virtual void resizePathBuffer(size_t sizeInBytes,
gpu::StorageBufferStructure) = 0;
virtual void resizePaintBuffer(size_t sizeInBytes,
gpu::StorageBufferStructure) = 0;
virtual void resizePaintAuxBuffer(size_t sizeInBytes,
gpu::StorageBufferStructure) = 0;
virtual void resizeContourBuffer(size_t sizeInBytes,
gpu::StorageBufferStructure) = 0;
virtual void resizeGradSpanBuffer(size_t sizeInBytes) = 0;
virtual void resizeTessVertexSpanBuffer(size_t sizeInBytes) = 0;
virtual void resizeTriangleVertexBuffer(size_t sizeInBytes) = 0;
virtual void resizeImageRectInstanceBuffer(size_t sizeInBytes) = 0;
virtual void resizeImageMeshInstanceBuffer(size_t sizeInBytes) = 0;
virtual void preBeginFrame(RenderContext*) {}
// Returns true if the render context should end the drawList with a batch
// of type DrawType::renderPassResolve (and set "manuallyResolved" in the
// flush descriptor).
// This may be used, e.g., to manually resolve MSAA or to transfer pixels
// from an offscreen texture back to the main render target.
virtual bool wantsManualRenderPassResolve(
gpu::InterlockMode,
const RenderTarget*,
const IAABB& renderTargetUpdateBounds,
uint32_t virtualTileWidth,
uint32_t virtualTileHeight,
gpu::DrawContents combinedDrawContents,
uint32_t msaaSampleCount) const
{
return false;
}
// Perform any bookkeeping or other tasks that need to run before
// RenderContext begins accessing GPU resources for the flush. (Update
// counters, advance buffer pools, etc.)
//
// The provided resource lifetime counters communicate how the client is
// performing CPU-GPU synchronization. Resources used during the upcoming
// flush will belong to 'nextFrameNumber'. Resources last used on or before
// 'safeFrameNumber' are safe to be released or recycled.
virtual void prepareToFlush(uint64_t nextFrameNumber,
uint64_t safeFrameNumber)
{}
// Map GPU buffers. (The implementation may wish to allocate the mappable
// buffers in rings, in order to avoid expensive synchronization with the
// GPU pipeline. See RenderContextBufferRingImpl.)
virtual void* mapFlushUniformBuffer(size_t mapSizeInBytes) = 0;
virtual void* mapPathBuffer(size_t mapSizeInBytes) = 0;
virtual void* mapPaintBuffer(size_t mapSizeInBytes) = 0;
virtual void* mapPaintAuxBuffer(size_t mapSizeInBytes) = 0;
virtual void* mapContourBuffer(size_t mapSizeInBytes) = 0;
virtual void* mapGradSpanBuffer(size_t mapSizeInBytes) = 0;
virtual void* mapTessVertexSpanBuffer(size_t mapSizeInBytes) = 0;
virtual void* mapTriangleVertexBuffer(size_t mapSizeInBytes) = 0;
virtual void* mapImageRectInstanceBuffer(size_t mapSizeInBytes) = 0;
virtual void* mapImageMeshInstanceBuffer(size_t mapSizeInBytes) = 0;
// Unmap GPU buffers. All buffers will be unmapped before flush().
virtual void unmapFlushUniformBuffer(size_t mapSizeInBytes) = 0;
virtual void unmapPathBuffer(size_t mapSizeInBytes) = 0;
virtual void unmapPaintBuffer(size_t mapSizeInBytes) = 0;
virtual void unmapPaintAuxBuffer(size_t mapSizeInBytes) = 0;
virtual void unmapContourBuffer(size_t mapSizeInBytes) = 0;
virtual void unmapGradSpanBuffer(size_t mapSizeInBytes) = 0;
virtual void unmapTessVertexSpanBuffer(size_t mapSizeInBytes) = 0;
virtual void unmapTriangleVertexBuffer(size_t mapSizeInBytes) = 0;
virtual void unmapImageRectInstanceBuffer(size_t mapSizeInBytes) = 0;
virtual void unmapImageMeshInstanceBuffer(size_t mapSizeInBytes) = 0;
// Allocate resources that are updated and used during flush().
virtual void resizeGradientTexture(uint32_t width, uint32_t height) = 0;
virtual void resizeTessellationTexture(uint32_t width, uint32_t height) = 0;
virtual void resizeFeatherAtlasTexture(uint32_t width, uint32_t height)
{
// Override this method to support atlas feathering.
assert(width == 0 && height == 0);
}
// Not all APIs support pure memoryless pixel local storage. This optional
// resource is a space to store PLS data that does not persist outside a
// render pass. (Namely, coverage, clip, and scratch.)
// NOTE: It is specified as a TEXTURE_2D_ARRAY because that gets better
// cache performance on Intel Arc than separate textures.
constexpr static uint32_t PLS_TRANSIENT_BACKING_MAX_PLANE_COUNT = 3;
virtual void resizeTransientPLSBacking(uint32_t width,
uint32_t height,
uint32_t planeCount)
{}
// Used in atomic mode. Similar to transient PLS backing, except it's a
// single 2D resource that also supports atomic operations.
virtual void resizeAtomicCoverageBacking(uint32_t width, uint32_t height) {}
virtual void resizeCoverageBuffer(size_t sizeInBytes)
{
// Override this method to support the experimental clockwiseAtomic
// mode.
assert(sizeInBytes == 0);
}
// Perform rendering in three steps:
//
// 1. Prepare the gradient texture:
// * Render the GradientSpan instances into the gradient texture.
// * Copy the TwoTexelRamp data directly into the gradient texture.
//
// 2. Render the TessVertexSpan instances into the tessellation texture.
//
// 3. Execute the draw list. (The Rive renderer shaders read the gradient
// and tessellation textures in order to do path rendering.)
//
// A single frame may have multiple logical flushes (and call flush()
// multiple times).
virtual void flush(const gpu::FlushDescriptor&) = 0;
// Called after all logical flushes in a frame have completed.
virtual void postFlush(const RenderContext::FlushResources&) {}
// Called after replayed Ore passes, before the renderer draws again. Ore
// leaves behind state a backend's own cache does not track, which the
// next flush would then skip updating and render black.
virtual void scrubStateAfterOre() {}
// Creates a platform-specific command buffer for use with flush().
// Returns an opaque pointer that should be passed as
// FlushResources::externalCommandBuffer.
// The default implementation returns nullptr (not supported).
virtual void* makeCommandBuffer() { return nullptr; }
// Commits a command buffer previously created by makeCommandBuffer().
// Called after flush() to submit the GPU work.
virtual void commitCommandBuffer(void* commandBuffer) {}
// Steady clock, used to determine when we should trim our resource
// allocations.
virtual double secondsNow() const = 0;
protected:
PlatformFeatures m_platformFeatures;
};
} // namespace rive::gpu