Add headless recording renderer api
This commit is contained in:
263
src/renderer_api/recording_renderer.cpp
Normal file
263
src/renderer_api/recording_renderer.cpp
Normal file
@@ -0,0 +1,263 @@
|
||||
#include "renderer_api/recording_renderer.h"
|
||||
|
||||
namespace pp::renderer {
|
||||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] const char* non_null_name(const char* name) noexcept
|
||||
{
|
||||
return name == nullptr ? "" : name;
|
||||
}
|
||||
|
||||
void push_command(
|
||||
std::vector<RecordedRenderCommand>* commands,
|
||||
RecordedRenderCommand command)
|
||||
{
|
||||
if (commands != nullptr) {
|
||||
commands->push_back(command);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RecordingTexture2D::RecordingTexture2D(TextureDesc desc) noexcept
|
||||
: desc_(desc)
|
||||
{
|
||||
}
|
||||
|
||||
TextureDesc RecordingTexture2D::desc() const noexcept
|
||||
{
|
||||
return desc_;
|
||||
}
|
||||
|
||||
RecordingRenderTarget::RecordingRenderTarget(TextureDesc color_desc) noexcept
|
||||
: color_desc_(color_desc)
|
||||
{
|
||||
}
|
||||
|
||||
TextureDesc RecordingRenderTarget::color_desc() const noexcept
|
||||
{
|
||||
return color_desc_;
|
||||
}
|
||||
|
||||
RecordingShaderProgram::RecordingShaderProgram(const char* debug_name) noexcept
|
||||
: debug_name_(non_null_name(debug_name))
|
||||
{
|
||||
}
|
||||
|
||||
const char* RecordingShaderProgram::debug_name() const noexcept
|
||||
{
|
||||
return debug_name_;
|
||||
}
|
||||
|
||||
RecordingMesh::RecordingMesh(MeshDesc desc) noexcept
|
||||
: desc_(desc)
|
||||
{
|
||||
}
|
||||
|
||||
MeshDesc RecordingMesh::desc() const noexcept
|
||||
{
|
||||
return desc_;
|
||||
}
|
||||
|
||||
RecordingReadbackBuffer::RecordingReadbackBuffer(std::uint64_t size_bytes) noexcept
|
||||
: size_bytes_(size_bytes)
|
||||
{
|
||||
}
|
||||
|
||||
std::uint64_t RecordingReadbackBuffer::size_bytes() const noexcept
|
||||
{
|
||||
return size_bytes_;
|
||||
}
|
||||
|
||||
RecordingCommandContext::RecordingCommandContext(std::vector<RecordedRenderCommand>& commands) noexcept
|
||||
: commands_(&commands)
|
||||
{
|
||||
}
|
||||
|
||||
pp::foundation::Status RecordingCommandContext::begin_render_pass(
|
||||
IRenderTarget& target,
|
||||
ClearColor clear_color) noexcept
|
||||
{
|
||||
if (in_render_pass_) {
|
||||
return pp::foundation::Status::invalid_argument("render pass is already active");
|
||||
}
|
||||
|
||||
active_target_ = target.color_desc();
|
||||
if (!active_target_.render_target) {
|
||||
return pp::foundation::Status::invalid_argument("render target texture must be flagged as render_target");
|
||||
}
|
||||
|
||||
const auto size_status = texture_byte_size(active_target_);
|
||||
if (!size_status.ok()) {
|
||||
return size_status.status();
|
||||
}
|
||||
|
||||
in_render_pass_ = true;
|
||||
shader_bound_ = false;
|
||||
mesh_bound_ = false;
|
||||
push_command(commands_, RecordedRenderCommand {
|
||||
.kind = RecordedRenderCommandKind::begin_render_pass,
|
||||
.target_desc = active_target_,
|
||||
.clear_color = clear_color,
|
||||
});
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status RecordingCommandContext::set_viewport(Viewport viewport) noexcept
|
||||
{
|
||||
if (!in_render_pass_) {
|
||||
return pp::foundation::Status::invalid_argument("render pass has not begun");
|
||||
}
|
||||
|
||||
const auto status = validate_viewport(viewport, active_target_.extent);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
push_command(commands_, RecordedRenderCommand {
|
||||
.kind = RecordedRenderCommandKind::set_viewport,
|
||||
.viewport = viewport,
|
||||
});
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status RecordingCommandContext::bind_shader(IShaderProgram& shader) noexcept
|
||||
{
|
||||
if (!in_render_pass_) {
|
||||
return pp::foundation::Status::invalid_argument("render pass has not begun");
|
||||
}
|
||||
|
||||
shader_bound_ = true;
|
||||
push_command(commands_, RecordedRenderCommand {
|
||||
.kind = RecordedRenderCommandKind::bind_shader,
|
||||
.name = shader.debug_name(),
|
||||
});
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status RecordingCommandContext::bind_mesh(IMesh& mesh) noexcept
|
||||
{
|
||||
if (!in_render_pass_) {
|
||||
return pp::foundation::Status::invalid_argument("render pass has not begun");
|
||||
}
|
||||
|
||||
const auto desc = mesh.desc();
|
||||
const auto status = validate_mesh_desc(desc);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
mesh_bound_ = true;
|
||||
push_command(commands_, RecordedRenderCommand {
|
||||
.kind = RecordedRenderCommandKind::bind_mesh,
|
||||
.mesh_desc = desc,
|
||||
});
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status RecordingCommandContext::draw() noexcept
|
||||
{
|
||||
if (!in_render_pass_) {
|
||||
return pp::foundation::Status::invalid_argument("render pass has not begun");
|
||||
}
|
||||
if (!shader_bound_) {
|
||||
return pp::foundation::Status::invalid_argument("shader must be bound before draw");
|
||||
}
|
||||
if (!mesh_bound_) {
|
||||
return pp::foundation::Status::invalid_argument("mesh must be bound before draw");
|
||||
}
|
||||
|
||||
push_command(commands_, RecordedRenderCommand {
|
||||
.kind = RecordedRenderCommandKind::draw,
|
||||
});
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
void RecordingCommandContext::end_render_pass() noexcept
|
||||
{
|
||||
if (!in_render_pass_) {
|
||||
return;
|
||||
}
|
||||
|
||||
push_command(commands_, RecordedRenderCommand {
|
||||
.kind = RecordedRenderCommandKind::end_render_pass,
|
||||
});
|
||||
in_render_pass_ = false;
|
||||
shader_bound_ = false;
|
||||
mesh_bound_ = false;
|
||||
}
|
||||
|
||||
bool RecordingCommandContext::in_render_pass() const noexcept
|
||||
{
|
||||
return in_render_pass_;
|
||||
}
|
||||
|
||||
RecordingRenderTrace::RecordingRenderTrace(std::vector<RecordedRenderCommand>& commands) noexcept
|
||||
: commands_(&commands)
|
||||
{
|
||||
}
|
||||
|
||||
void RecordingRenderTrace::marker(const char* component, const char* name) noexcept
|
||||
{
|
||||
push_command(commands_, RecordedRenderCommand {
|
||||
.kind = RecordedRenderCommandKind::trace_marker,
|
||||
.component = non_null_name(component),
|
||||
.name = non_null_name(name),
|
||||
});
|
||||
}
|
||||
|
||||
RecordingRenderDevice::RecordingRenderDevice() noexcept
|
||||
: context_(commands_)
|
||||
, trace_(commands_)
|
||||
{
|
||||
}
|
||||
|
||||
const char* RecordingRenderDevice::backend_name() const noexcept
|
||||
{
|
||||
return "recording";
|
||||
}
|
||||
|
||||
ICommandContext& RecordingRenderDevice::immediate_context() noexcept
|
||||
{
|
||||
return context_;
|
||||
}
|
||||
|
||||
IRenderTrace* RecordingRenderDevice::trace() noexcept
|
||||
{
|
||||
return &trace_;
|
||||
}
|
||||
|
||||
std::span<const RecordedRenderCommand> RecordingRenderDevice::commands() const noexcept
|
||||
{
|
||||
return commands_;
|
||||
}
|
||||
|
||||
void RecordingRenderDevice::clear() noexcept
|
||||
{
|
||||
commands_.clear();
|
||||
}
|
||||
|
||||
const char* recorded_render_command_kind_name(RecordedRenderCommandKind kind) noexcept
|
||||
{
|
||||
switch (kind) {
|
||||
case RecordedRenderCommandKind::begin_render_pass:
|
||||
return "begin_render_pass";
|
||||
case RecordedRenderCommandKind::set_viewport:
|
||||
return "set_viewport";
|
||||
case RecordedRenderCommandKind::bind_shader:
|
||||
return "bind_shader";
|
||||
case RecordedRenderCommandKind::bind_mesh:
|
||||
return "bind_mesh";
|
||||
case RecordedRenderCommandKind::draw:
|
||||
return "draw";
|
||||
case RecordedRenderCommandKind::end_render_pass:
|
||||
return "end_render_pass";
|
||||
case RecordedRenderCommandKind::trace_marker:
|
||||
return "trace_marker";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user