rhi: Remove Pipeline, Uniform/BindingSet, add dynamic state

The pipeline abstraction mimicked Vulkan and d3d12 explicit pipeline state objects
but, like GraphicsContext, was ill-considered for the kinds of drawing
behavior SRB2 uses. Rather than push this complexity into the drawing
code, it will instead be the responsibility of the backend to manage
explicit pipeline objects if necessary.

Now, drawing code must create a Program instance, bind it, and set the
rasterizer state to do the equivalent. Uniform, sampler, and vertex
attribute bindings are significantly simpler to set up now.
This commit is contained in:
Eidolon 2024-10-26 13:49:22 -05:00
parent 4499979458
commit 136761cf3b
17 changed files with 795 additions and 1275 deletions

View file

@ -10,11 +10,14 @@
#include "blit_postimg_screens.hpp"
#include <cstddef>
#include <glm/mat3x3.hpp>
#include <glm/mat4x4.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <tcb/span.hpp>
#include "../p_tick.h"
#include "../i_time.h"
@ -36,34 +39,6 @@ struct BlitVertex
};
} // namespace
static const PipelineDesc kPostimgPipelineDesc =
{
PipelineProgram::kPostimg,
{{{sizeof(BlitVertex)}}, {{VertexAttributeName::kPosition, 0, 0}, {VertexAttributeName::kTexCoord0, 0, 12}}},
{{{{UniformName::kTime, UniformName::kProjection, UniformName::kModelView, UniformName::kTexCoord0Transform, UniformName::kTexCoord0Min, UniformName::kTexCoord0Max, UniformName::kPostimgWater, UniformName::kPostimgHeat}}}},
{{SamplerName::kSampler0}},
std::nullopt,
{std::nullopt, {true, true, true, true}},
PrimitiveType::kTriangles,
CullMode::kNone,
FaceWinding::kCounterClockwise,
{0.0, 0.0, 0.0, 1.0}
};
static const PipelineDesc kPostimgIndexedPipelineDesc =
{
PipelineProgram::kPostimg,
{{{sizeof(BlitVertex)}}, {{VertexAttributeName::kPosition, 0, 0}, {VertexAttributeName::kTexCoord0, 0, 12}}},
{{{{UniformName::kTime, UniformName::kProjection, UniformName::kModelView, UniformName::kTexCoord0Transform, UniformName::kTexCoord0Min, UniformName::kTexCoord0Max, UniformName::kPostimgWater, UniformName::kPostimgHeat}}}},
{{SamplerName::kSampler0, SamplerName::kSampler1}},
std::nullopt,
{std::nullopt, {true, true, true, true}},
PrimitiveType::kTriangles,
CullMode::kNone,
FaceWinding::kCounterClockwise,
{0.0, 0.0, 0.0, 1.0}
};
static const BlitVertex kVerts[] =
{{-.5f, -.5f, 0.f, 0.f, 0.f}, {.5f, -.5f, 0.f, 1.f, 0.f}, {-.5f, .5f, 0.f, 0.f, 1.f}, {.5f, .5f, 0.f, 1.f, 1.f}};
@ -106,27 +81,81 @@ void BlitPostimgScreens::draw(Rhi& rhi)
for (uint32_t i = 0; i < screens_; i++)
{
BlitPostimgScreens::ScreenConfig& screen_config = screen_configs_[i];
BlitPostimgScreens::ScreenData& data = screen_data_[i];
rhi.bind_pipeline(data.pipeline);
glm::mat4 projection = glm::scale(glm::identity<glm::mat4>(), glm::vec3(2.f, -2.f, 1.f));
glm::mat4 modelview = glm::identity<glm::mat4>();
glm::vec2 flip_mirror_uv_displace {0.0, 0.0};
if (screen_config.post.mirror)
{
flip_mirror_uv_displace.x = 1 - (1 - screen_config.uv_size.x);
}
if (screen_config.post.flip)
{
flip_mirror_uv_displace.y = 1 - (1 - screen_config.uv_size.y);
}
glm::mat3 texcoord_transform =
{
glm::vec3(screen_config.uv_size.x * (screen_config.post.mirror ? -1 : 1), 0.0, 0.0),
glm::vec3(0.0, screen_config.uv_size.y * (screen_config.post.flip ? -1 : 1), 0.0),
glm::vec3(screen_config.uv_offset + flip_mirror_uv_displace, 1.0)
};
glm::vec2 texcoord_min = screen_config.uv_offset;
glm::vec2 texcoord_max = screen_config.uv_offset + screen_config.uv_size;
RasterizerStateDesc r_state {};
r_state.cull = CullMode::kNone;
rhi.bind_program(data.program);
rhi.set_rasterizer_state(r_state);
rhi.set_viewport(get_screen_viewport(i, screens_, target_width_, target_height_));
rhi.bind_uniform_set(0, data.uniform_set);
rhi.bind_binding_set(data.binding_set);
rhi.bind_vertex_attrib("a_position", quad_vbo_, rhi::VertexAttributeFormat::kFloat3, offsetof(BlitVertex, x), sizeof(BlitVertex));
rhi.bind_vertex_attrib("a_texcoord0", quad_vbo_, rhi::VertexAttributeFormat::kFloat2, offsetof(BlitVertex, u), sizeof(BlitVertex));
rhi.bind_index_buffer(quad_ibo_);
rhi.set_uniform("u_time", static_cast<float>(leveltime));
rhi.set_uniform("u_projection", projection);
rhi.set_uniform("u_modelview", modelview);
rhi.set_uniform("u_texcoord0_transform", texcoord_transform);
rhi.set_uniform("u_texcoord0_min", texcoord_min);
rhi.set_uniform("u_texcoord0_max", texcoord_max);
rhi.set_uniform("u_postimg_water", screen_config.post.water);
rhi.set_uniform("u_postimg_heat", screen_config.post.heat);
rhi.set_sampler("s_sampler0", 0, screen_config.source);
if (screen_config.indexed)
{
rhi.set_sampler("s_sampler1", 1, palette_mgr_->palette());
}
rhi.draw_indexed(6, 0);
}
}
void BlitPostimgScreens::prepass(Rhi& rhi)
{
if (!pipeline_)
if (!program_)
{
pipeline_ = rhi.create_pipeline(kPostimgPipelineDesc);
static const char* defines[1] = {
"ENABLE_S_SAMPLER0"
};
ProgramDesc desc {};
desc.name = "postimg";
desc.defines = tcb::make_span(defines);
program_ = rhi.create_program(desc);
}
if (!indexed_pipeline_)
if (!indexed_program_)
{
indexed_pipeline_ = rhi.create_pipeline(kPostimgIndexedPipelineDesc);
static const char* defines[2] = {
"ENABLE_S_SAMPLER0",
"ENABLE_S_SAMPLER1"
};
ProgramDesc desc {};
desc.name = "postimg";
desc.defines = tcb::make_span(defines);
program_ = rhi.create_program(desc);
}
if (!quad_vbo_)
@ -161,65 +190,13 @@ void BlitPostimgScreens::transfer(Rhi& rhi)
if (screen_config.indexed)
{
data.pipeline = indexed_pipeline_;
data.program = program_;
}
else
{
data.pipeline = pipeline_;
data.program = indexed_program_;
}
VertexAttributeBufferBinding vertex_bindings[] = {{0, quad_vbo_}};
TextureBinding sampler_bindings[] =
{
{SamplerName::kSampler0, screen_config.source},
{SamplerName::kSampler1, palette_mgr_->palette()}
};
data.binding_set = rhi.create_binding_set(
data.pipeline,
{
vertex_bindings,
tcb::span(sampler_bindings, screen_config.indexed ? 2 : 1)
}
);
glm::mat4 projection = glm::scale(glm::identity<glm::mat4>(), glm::vec3(2.f, -2.f, 1.f));
glm::mat4 modelview = glm::identity<glm::mat4>();
glm::vec2 flip_mirror_uv_displace {0.0, 0.0};
if (screen_config.post.mirror)
{
flip_mirror_uv_displace.x = 1 - (1 - screen_config.uv_size.x);
}
if (screen_config.post.flip)
{
flip_mirror_uv_displace.y = 1 - (1 - screen_config.uv_size.y);
}
glm::mat3 texcoord_transform =
{
glm::vec3(screen_config.uv_size.x * (screen_config.post.mirror ? -1 : 1), 0.0, 0.0),
glm::vec3(0.0, screen_config.uv_size.y * (screen_config.post.flip ? -1 : 1), 0.0),
glm::vec3(screen_config.uv_offset + flip_mirror_uv_displace, 1.0)
};
glm::vec2 texcoord_min = screen_config.uv_offset;
glm::vec2 texcoord_max = screen_config.uv_offset + screen_config.uv_size;
UniformVariant uniforms[] =
{
static_cast<float>(leveltime),
projection,
modelview,
texcoord_transform,
texcoord_min,
texcoord_max,
screen_config.post.water,
screen_config.post.heat
};
data.uniform_set = rhi.create_uniform_set({uniforms});
screen_data_[i] = std::move(data);
}
}

View file

@ -46,13 +46,11 @@ public:
private:
struct ScreenData
{
rhi::Handle<rhi::Pipeline> pipeline;
rhi::Handle<rhi::BindingSet> binding_set;
rhi::Handle<rhi::UniformSet> uniform_set;
rhi::Handle<rhi::Program> program;
};
rhi::Handle<rhi::Pipeline> pipeline_;
rhi::Handle<rhi::Pipeline> indexed_pipeline_;
rhi::Handle<rhi::Program> program_;
rhi::Handle<rhi::Program> indexed_program_;
rhi::Handle<rhi::Buffer> quad_vbo_;
rhi::Handle<rhi::Buffer> quad_ibo_;
bool upload_quad_buffer_;

View file

@ -11,6 +11,7 @@
#include "blit_rect.hpp"
#include <optional>
#include <vector>
#include <glm/gtc/matrix_transform.hpp>
#include <tcb/span.hpp>
@ -38,62 +39,6 @@ static const BlitVertex kVerts[] =
static const uint16_t kIndices[] = {0, 1, 2, 1, 3, 2};
/// @brief Pipeline used for non-paletted source textures.
static const PipelineDesc kUnshadedPipelineDescription = {
PipelineProgram::kUnshaded,
{{{sizeof(BlitVertex)}}, {{VertexAttributeName::kPosition, 0, 0}, {VertexAttributeName::kTexCoord0, 0, 12}}},
{{{UniformName::kProjection}, {{UniformName::kModelView, UniformName::kTexCoord0Transform}}}},
{{// RGB/A texture
SamplerName::kSampler0}},
std::nullopt,
{std::nullopt, {true, true, true, true}},
PrimitiveType::kTriangles,
CullMode::kNone,
FaceWinding::kCounterClockwise,
{0.f, 0.f, 0.f, 1.f}};
/// @brief Pipeline used for sharp bilinear special blit.
static const PipelineDesc kSharpBilinearPipelineDescription = {
PipelineProgram::kSharpBilinear,
{{{sizeof(BlitVertex)}}, {{VertexAttributeName::kPosition, 0, 0}, {VertexAttributeName::kTexCoord0, 0, 12}}},
{{{UniformName::kProjection}, {{UniformName::kModelView, UniformName::kTexCoord0Transform, UniformName::kSampler0Size}}}},
{{// RGB/A texture
SamplerName::kSampler0}},
std::nullopt,
{std::nullopt, {true, true, true, true}},
PrimitiveType::kTriangles,
CullMode::kNone,
FaceWinding::kCounterClockwise,
{0.f, 0.f, 0.f, 1.f}};
/// @brief Pipeline used for CRT special blit
static const PipelineDesc kCrtPipelineDescription = {
PipelineProgram::kCrt,
{{{sizeof(BlitVertex)}}, {{VertexAttributeName::kPosition, 0, 0}, {VertexAttributeName::kTexCoord0, 0, 12}}},
{{{UniformName::kProjection}, {{UniformName::kModelView, UniformName::kTexCoord0Transform, UniformName::kSampler0Size}}}},
{{// RGB/A texture
SamplerName::kSampler0, SamplerName::kSampler1}},
std::nullopt,
{std::nullopt, {true, true, true, true}},
PrimitiveType::kTriangles,
CullMode::kNone,
FaceWinding::kCounterClockwise,
{0.f, 0.f, 0.f, 1.f}};
/// @brief Pipeline used for CRT special blit (sharp)
static const PipelineDesc kCrtSharpPipelineDescription = {
PipelineProgram::kCrtSharp,
{{{sizeof(BlitVertex)}}, {{VertexAttributeName::kPosition, 0, 0}, {VertexAttributeName::kTexCoord0, 0, 12}}},
{{{UniformName::kProjection}, {{UniformName::kModelView, UniformName::kTexCoord0Transform, UniformName::kSampler0Size}}}},
{{// RGB/A texture
SamplerName::kSampler0, SamplerName::kSampler1}},
std::nullopt,
{std::nullopt, {true, true, true, true}},
PrimitiveType::kTriangles,
CullMode::kNone,
FaceWinding::kCounterClockwise,
{0.f, 0.f, 0.f, 1.f}};
BlitRectPass::BlitRectPass() : BlitRectPass(BlitRectPass::BlitMode::kNearest) {}
BlitRectPass::BlitRectPass(BlitRectPass::BlitMode blit_mode) : blit_mode_(blit_mode) {}
BlitRectPass::~BlitRectPass() = default;
@ -107,26 +52,29 @@ void BlitRectPass::draw(Rhi& rhi)
void BlitRectPass::prepass(Rhi& rhi)
{
if (!pipeline_)
if (!program_)
{
ProgramDesc desc {};
const char* defines[1] = {"ENABLE_VA_TEXCOORD0"};
desc.defines = tcb::make_span(defines);
switch (blit_mode_)
{
case BlitRectPass::BlitMode::kNearest:
pipeline_ = rhi.create_pipeline(kUnshadedPipelineDescription);
break;
case BlitRectPass::BlitMode::kSharpBilinear:
pipeline_ = rhi.create_pipeline(kSharpBilinearPipelineDescription);
break;
case BlitRectPass::BlitMode::kCrt:
pipeline_ = rhi.create_pipeline(kCrtPipelineDescription);
break;
case BlitRectPass::BlitMode::kCrtSharp:
pipeline_ = rhi.create_pipeline(kCrtSharpPipelineDescription);
break;
default:
std::terminate();
case BlitRectPass::BlitMode::kNearest:
desc.name = "unshaded";
break;
case BlitRectPass::BlitMode::kSharpBilinear:
desc.name = "sharpbilinear";
break;
case BlitRectPass::BlitMode::kCrt:
desc.name = "crt";
break;
case BlitRectPass::BlitMode::kCrtSharp:
desc.name = "crtsharp";
break;
default:
std::terminate();
}
program_ = rhi.create_program(desc);
}
if (!quad_vbo_)
@ -230,6 +178,18 @@ void BlitRectPass::transfer(Rhi& rhi)
rhi.update_texture(dot_pattern_, {0, 0, 12, 4}, PixelFormat::kRGBA8, tcb::as_bytes(tcb::span(kDotPattern)));
dot_pattern_needs_upload_ = false;
}
}
void BlitRectPass::graphics(Rhi& rhi)
{
rhi.bind_program(program_);
RasterizerStateDesc rs {};
rs.cull = CullMode::kNone;
rhi.set_rasterizer_state(rs);
rhi.bind_vertex_attrib("a_position", quad_vbo_, VertexAttributeFormat::kFloat3, offsetof(BlitVertex, x), sizeof(BlitVertex));
rhi.bind_vertex_attrib("a_texcoord0", quad_vbo_, VertexAttributeFormat::kFloat2, offsetof(BlitVertex, u), sizeof(BlitVertex));
float aspect = 1.0;
float output_aspect = 1.0;
@ -242,122 +202,46 @@ void BlitRectPass::transfer(Rhi& rhi)
rhi::TextureDetails texture_details = rhi.get_texture_details(texture_);
std::array<rhi::UniformVariant, 1> g1_uniforms = {{
// Projection
glm::scale(
glm::identity<glm::mat4>(),
glm::vec3(taller ? 1.f : 1.f / output_aspect, taller ? -1.f / (1.f / output_aspect) : -1.f, 1.f)
)
}};
glm::mat4 projection = glm::scale(
glm::identity<glm::mat4>(),
glm::vec3(taller ? 1.f : 1.f / output_aspect, taller ? -1.f / (1.f / output_aspect) : -1.f, 1.f)
);
glm::mat4 modelview = glm::scale(
glm::identity<glm::mat4>(),
glm::vec3(taller ? 2.f : 2.f * aspect, taller ? 2.f * (1.f / aspect) : 2.f, 1.f)
);;
glm::mat3 texcoord0_transform = glm::mat3(
glm::vec3(1.f, 0.f, 0.f),
glm::vec3(0.f, output_flip_ ? -1.f : 1.f, 0.f),
glm::vec3(0.f, output_flip_ ? 1.f : 0.f, 1.f)
);
glm::vec2 sampler0_size = glm::vec2(texture_details.width, texture_details.height);
uniform_sets_[0] = rhi.create_uniform_set({g1_uniforms});
rhi.set_uniform("u_projection", projection);
rhi.set_uniform("u_modelview", modelview);
rhi.set_uniform("u_texcoord0_transform", texcoord0_transform);
switch (blit_mode_)
{
case BlitRectPass::BlitMode::kCrt:
{
std::array<rhi::UniformVariant, 3> g2_uniforms = {
// ModelView
glm::scale(
glm::identity<glm::mat4>(),
glm::vec3(taller ? 2.f : 2.f * aspect, taller ? 2.f * (1.f / aspect) : 2.f, 1.f)
),
// Texcoord0 Transform
glm::mat3(
glm::vec3(1.f, 0.f, 0.f),
glm::vec3(0.f, output_flip_ ? -1.f : 1.f, 0.f),
glm::vec3(0.f, output_flip_ ? 1.f : 0.f, 1.f)
),
// Sampler 0 Size
glm::vec2(texture_details.width, texture_details.height)
};
uniform_sets_[1] = rhi.create_uniform_set({g2_uniforms});
std::array<rhi::VertexAttributeBufferBinding, 1> vbs = {{{0, quad_vbo_}}};
std::array<rhi::TextureBinding, 2> tbs = {{{rhi::SamplerName::kSampler0, texture_}, {rhi::SamplerName::kSampler1, dot_pattern_}}};
binding_set_ = rhi.create_binding_set(pipeline_, {vbs, tbs});
case BlitRectPass::BlitMode::kNearest:
break;
}
case BlitRectPass::BlitMode::kCrtSharp:
{
std::array<rhi::UniformVariant, 3> g2_uniforms = {
// ModelView
glm::scale(
glm::identity<glm::mat4>(),
glm::vec3(taller ? 2.f : 2.f * aspect, taller ? 2.f * (1.f / aspect) : 2.f, 1.f)
),
// Texcoord0 Transform
glm::mat3(
glm::vec3(1.f, 0.f, 0.f),
glm::vec3(0.f, output_flip_ ? -1.f : 1.f, 0.f),
glm::vec3(0.f, output_flip_ ? 1.f : 0.f, 1.f)
),
// Sampler 0 Size
glm::vec2(texture_details.width, texture_details.height)
};
uniform_sets_[1] = rhi.create_uniform_set({g2_uniforms});
std::array<rhi::VertexAttributeBufferBinding, 1> vbs = {{{0, quad_vbo_}}};
std::array<rhi::TextureBinding, 2> tbs = {{{rhi::SamplerName::kSampler0, texture_}, {rhi::SamplerName::kSampler1, dot_pattern_}}};
binding_set_ = rhi.create_binding_set(pipeline_, {vbs, tbs});
break;
}
case BlitRectPass::BlitMode::kSharpBilinear:
{
std::array<rhi::UniformVariant, 3> g2_uniforms = {
// ModelView
glm::scale(
glm::identity<glm::mat4>(),
glm::vec3(taller ? 2.f : 2.f * aspect, taller ? 2.f * (1.f / aspect) : 2.f, 1.f)
),
// Texcoord0 Transform
glm::mat3(
glm::vec3(1.f, 0.f, 0.f),
glm::vec3(0.f, output_flip_ ? -1.f : 1.f, 0.f),
glm::vec3(0.f, output_flip_ ? 1.f : 0.f, 1.f)
),
// Sampler0 size
glm::vec2(texture_details.width, texture_details.height)
};
uniform_sets_[1] = rhi.create_uniform_set({g2_uniforms});
std::array<rhi::VertexAttributeBufferBinding, 1> vbs = {{{0, quad_vbo_}}};
std::array<rhi::TextureBinding, 1> tbs = {{{rhi::SamplerName::kSampler0, texture_}}};
binding_set_ = rhi.create_binding_set(pipeline_, {vbs, tbs});
break;
}
default:
{
std::array<rhi::UniformVariant, 2> g2_uniforms = {
// ModelView
glm::scale(
glm::identity<glm::mat4>(),
glm::vec3(taller ? 2.f : 2.f * aspect, taller ? 2.f * (1.f / aspect) : 2.f, 1.f)
),
// Texcoord0 Transform
glm::mat3(
glm::vec3(1.f, 0.f, 0.f),
glm::vec3(0.f, output_flip_ ? -1.f : 1.f, 0.f),
glm::vec3(0.f, output_flip_ ? 1.f : 0.f, 1.f)
)
};
uniform_sets_[1] = rhi.create_uniform_set({g2_uniforms});
std::array<rhi::VertexAttributeBufferBinding, 1> vbs = {{{0, quad_vbo_}}};
std::array<rhi::TextureBinding, 1> tbs = {{{rhi::SamplerName::kSampler0, texture_}}};
binding_set_ = rhi.create_binding_set(pipeline_, {vbs, tbs});
rhi.set_uniform("u_sampler0_size", sampler0_size);
break;
}
rhi.set_sampler("s_sampler0", 0, texture_);
switch (blit_mode_)
{
case BlitRectPass::BlitMode::kCrt:
rhi.set_sampler("s_sampler1", 1, dot_pattern_);
break;
case BlitRectPass::BlitMode::kCrtSharp:
rhi.set_sampler("s_sampler1", 1, dot_pattern_);
break;
default:
break;
}
}
void BlitRectPass::graphics(Rhi& rhi)
{
rhi.bind_pipeline(pipeline_);
rhi.set_viewport(output_position_);
rhi.bind_uniform_set(0, uniform_sets_[0]);
rhi.bind_uniform_set(1, uniform_sets_[1]);
rhi.bind_binding_set(binding_set_);
rhi.bind_index_buffer(quad_ibo_);
rhi.draw_indexed(6, 0);
}

View file

@ -32,7 +32,7 @@ public:
};
private:
rhi::Handle<rhi::Pipeline> pipeline_;
rhi::Handle<rhi::Program> program_;
rhi::Handle<rhi::Texture> texture_;
uint32_t texture_width_ = 0;
uint32_t texture_height_ = 0;
@ -42,8 +42,6 @@ private:
bool output_flip_ = false;
rhi::Handle<rhi::Buffer> quad_vbo_;
rhi::Handle<rhi::Buffer> quad_ibo_;
std::array<rhi::Handle<rhi::UniformSet>, 2> uniform_sets_;
rhi::Handle<rhi::BindingSet> binding_set_;
BlitMode blit_mode_;
rhi::Handle<rhi::Texture> dot_pattern_;

View file

@ -11,6 +11,7 @@
#include "pass_imgui.hpp"
#include <imgui.h>
#include <tcb/span.hpp>
#include "../v_video.h"
@ -18,28 +19,6 @@ using namespace srb2;
using namespace srb2::hwr2;
using namespace srb2::rhi;
static const PipelineDesc kPipelineDesc = {
PipelineProgram::kUnshaded,
{{{sizeof(ImDrawVert)}},
{{VertexAttributeName::kPosition, 0, 0},
{VertexAttributeName::kTexCoord0, 0, 12},
{VertexAttributeName::kColor, 0, 24}}},
{{{UniformName::kProjection}, {{UniformName::kModelView, UniformName::kTexCoord0Transform}}}},
{{SamplerName::kSampler0}},
PipelineDepthStencilStateDesc {true, true, CompareFunc::kAlways, false, {}, {}},
{BlendDesc {
BlendFactor::kSourceAlpha,
BlendFactor::kOneMinusSourceAlpha,
BlendFunction::kAdd,
BlendFactor::kOne,
BlendFactor::kOneMinusSourceAlpha,
BlendFunction::kAdd},
{true, true, true, true}},
PrimitiveType::kTriangles,
CullMode::kNone,
FaceWinding::kCounterClockwise,
{0.f, 0.f, 0.f, 1.f}};
ImguiPass::ImguiPass()
{
}
@ -48,9 +27,16 @@ ImguiPass::~ImguiPass() = default;
void ImguiPass::prepass(Rhi& rhi)
{
if (!pipeline_)
if (!program_)
{
pipeline_ = rhi.create_pipeline(kPipelineDesc);
const char* defines[2] = {
"ENABLE_VA_TEXCOORD0",
"ENABLE_VA_COLOR"
};
ProgramDesc desc;
desc.name = "unshaded";
desc.defines = tcb::make_span(defines);
program_ = rhi.create_program(desc);
}
ImGuiIO& io = ImGui::GetIO();
@ -166,65 +152,67 @@ void ImguiPass::transfer(Rhi& rhi)
tcb::span<ImDrawIdx> index_span = tcb::span(im_list->IdxBuffer.Data, im_list->IdxBuffer.size());
rhi.update_buffer(ibo, 0, tcb::as_bytes(index_span));
// Uniform sets
std::array<UniformVariant, 1> g1_uniforms = {
// Projection
glm::mat4(
glm::vec4(2.f / vid.realwidth, 0.f, 0.f, 0.f),
glm::vec4(0.f, 2.f / vid.realheight, 0.f, 0.f),
glm::vec4(0.f, 0.f, 1.f, 0.f),
glm::vec4(-1.f, 1.f, 0.f, 1.f)
)
};
std::array<UniformVariant, 2> g2_uniforms = {
// ModelView
glm::mat4(
glm::vec4(1.f, 0.f, 0.f, 0.f),
glm::vec4(0.f, -1.f, 0.f, 0.f),
glm::vec4(0.f, 0.f, 1.f, 0.f),
glm::vec4(0.f, 0, 0.f, 1.f)
),
// Texcoord0 Transform
glm::mat3(
glm::vec3(1.f, 0.f, 0.f),
glm::vec3(0.f, 1.f, 0.f),
glm::vec3(0.f, 0.f, 1.f)
)
};
Handle<UniformSet> us_1 = rhi.create_uniform_set({g1_uniforms});
Handle<UniformSet> us_2 = rhi.create_uniform_set({g2_uniforms});
draw_list.us_1 = us_1;
draw_list.us_2 = us_2;
for (auto& draw_cmd : draw_list.cmds)
{
// Binding set
std::array<rhi::VertexAttributeBufferBinding, 1> vbos = {{{0, vbo}}};
std::array<rhi::TextureBinding, 1> tbs = {{{rhi::SamplerName::kSampler0, draw_cmd.tex}}};
rhi::Handle<rhi::BindingSet> binding_set = rhi.create_binding_set(pipeline_, {vbos, tbs});
draw_cmd.binding_set = binding_set;
draw_cmd.vbo = vbo;
draw_cmd.ibo = ibo;
draw_cmd.tex = draw_cmd.tex;
}
}
}
void ImguiPass::graphics(Rhi& rhi)
{
rhi.begin_default_render_pass(false);
rhi.bind_pipeline(pipeline_);
rhi.bind_program(program_);
RasterizerStateDesc rs;
rs.cull = CullMode::kNone;
rs.winding = FaceWinding::kCounterClockwise;
rs.depth_test = true;
rs.depth_func = CompareFunc::kAlways;
rs.blend_enabled = true;
rs.blend_source_factor_color = BlendFactor::kSourceAlpha;
rs.blend_dest_factor_color = BlendFactor::kOneMinusSourceAlpha;
rs.blend_color_function = BlendFunction::kAdd;
rs.blend_source_factor_alpha = BlendFactor::kOne;
rs.blend_dest_factor_alpha = BlendFactor::kOneMinusSourceAlpha;
rs.blend_alpha_function = BlendFunction::kAdd;
for (auto& draw_list : draw_lists_)
{
rhi.bind_uniform_set(0, draw_list.us_1);
rhi.bind_uniform_set(1, draw_list.us_2);
glm::mat4 projection = glm::mat4(
glm::vec4(2.f / vid.realwidth, 0.f, 0.f, 0.f),
glm::vec4(0.f, 2.f / vid.realheight, 0.f, 0.f),
glm::vec4(0.f, 0.f, 1.f, 0.f),
glm::vec4(-1.f, 1.f, 0.f, 1.f)
);
glm::mat4 modelview = glm::mat4(
glm::vec4(1.f, 0.f, 0.f, 0.f),
glm::vec4(0.f, -1.f, 0.f, 0.f),
glm::vec4(0.f, 0.f, 1.f, 0.f),
glm::vec4(0.f, 0, 0.f, 1.f)
);
glm::mat3 texcoord0_transform = glm::mat3(
glm::vec3(1.f, 0.f, 0.f),
glm::vec3(0.f, 1.f, 0.f),
glm::vec3(0.f, 0.f, 1.f)
);
rhi.set_uniform("u_projection", projection);
rhi.set_uniform("u_modelview", modelview);
rhi.set_uniform("u_texcoord0_transform", texcoord0_transform);
for (auto& cmd : draw_list.cmds)
{
rhi.bind_binding_set(cmd.binding_set);
rs.scissor_test = true;
rs.scissor = cmd.clip;
rhi.set_rasterizer_state(rs);
rhi.bind_vertex_attrib("a_position", cmd.vbo, VertexAttributeFormat::kFloat3, offsetof(ImDrawVert, pos), sizeof(ImDrawVert));
rhi.bind_vertex_attrib("a_texcoord0", cmd.vbo, VertexAttributeFormat::kFloat2, offsetof(ImDrawVert, uv), sizeof(ImDrawVert));
rhi.bind_vertex_attrib("a_color", cmd.vbo, VertexAttributeFormat::kFloat4, offsetof(ImDrawVert, colf), sizeof(ImDrawVert));
rhi.set_sampler("s_sampler0", 0, cmd.tex);
rhi.bind_index_buffer(draw_list.ibo);
rhi.set_scissor(cmd.clip);
rhi.draw_indexed(cmd.elems, cmd.i_offset);
}
}
rhi.end_render_pass();
}
void ImguiPass::postpass(Rhi& rhi)

View file

@ -22,24 +22,23 @@ class ImguiPass final
{
struct DrawCmd
{
rhi::Handle<rhi::Texture> tex;
uint32_t v_offset;
uint32_t elems;
uint32_t i_offset;
rhi::Rect clip;
rhi::Handle<rhi::BindingSet> binding_set;
rhi::Handle<rhi::Buffer> vbo;
rhi::Handle<rhi::Buffer> ibo;
rhi::Handle<rhi::Texture> tex;
};
struct DrawList
{
void* list;
rhi::Handle<rhi::Buffer> vbo;
rhi::Handle<rhi::Buffer> ibo;
rhi::Handle<rhi::UniformSet> us_1;
rhi::Handle<rhi::UniformSet> us_2;
std::vector<DrawCmd> cmds;
};
rhi::Handle<rhi::Pipeline> pipeline_;
rhi::Handle<rhi::Program> program_;
rhi::Handle<rhi::Texture> font_atlas_;
std::vector<DrawList> draw_lists_;

View file

@ -41,22 +41,6 @@ static const uint16_t kPostprocessIndices[] = {0, 1, 2, 1, 3, 2};
} // namespace
static const PipelineDesc kWipePipelineDesc = {
PipelineProgram::kPostprocessWipe,
{{{sizeof(PostprocessVertex)}},
{
{VertexAttributeName::kPosition, 0, 0},
{VertexAttributeName::kTexCoord0, 0, 12},
}},
{{{{UniformName::kProjection, UniformName::kWipeColorizeMode, UniformName::kWipeEncoreSwizzle}}}},
{{SamplerName::kSampler0, SamplerName::kSampler1, SamplerName::kSampler2}},
std::nullopt,
{std::nullopt, {true, true, true, true}},
PrimitiveType::kTriangles,
CullMode::kNone,
FaceWinding::kCounterClockwise,
{0.f, 0.f, 0.f, 1.f}};
PostprocessWipePass::PostprocessWipePass()
{
}
@ -73,9 +57,12 @@ void PostprocessWipePass::draw(Rhi& rhi)
void PostprocessWipePass::prepass(Rhi& rhi)
{
if (!pipeline_)
if (!program_)
{
pipeline_ = rhi.create_pipeline(kWipePipelineDesc);
ProgramDesc desc;
desc.name = "postprocesswipe";
desc.defines = tcb::span<const char*>();
program_ = rhi.create_program(desc);
}
if (!vbo_)
@ -195,20 +182,6 @@ void PostprocessWipePass::transfer(Rhi& rhi)
tcb::span<const std::byte> data = tcb::as_bytes(tcb::span(mask_data_));
rhi.update_texture(wipe_tex_, {0, 0, mask_w_, mask_h_}, PixelFormat::kR8, data);
UniformVariant uniforms[] = {
glm::scale(glm::identity<glm::mat4>(), glm::vec3(2.f, 2.f, 1.f)),
static_cast<int32_t>(wipe_color_mode_),
static_cast<int32_t>(wipe_swizzle_)
};
us_ = rhi.create_uniform_set({tcb::span(uniforms)});
VertexAttributeBufferBinding vbos[] = {{0, vbo_}};
TextureBinding tx[] = {
{SamplerName::kSampler0, start_},
{SamplerName::kSampler1, end_},
{SamplerName::kSampler2, wipe_tex_}};
bs_ = rhi.create_binding_set(pipeline_, {vbos, tx});
}
void PostprocessWipePass::graphics(Rhi& rhi)
@ -218,10 +191,21 @@ void PostprocessWipePass::graphics(Rhi& rhi)
return;
}
rhi.bind_pipeline(pipeline_);
rhi.bind_program(program_);
RasterizerStateDesc desc {};
desc.cull = CullMode::kNone;
rhi.set_rasterizer_state(desc);
rhi.set_uniform("u_projection", glm::scale(glm::identity<glm::mat4>(), glm::vec3(2.f, 2.f, 1.f)));
rhi.set_uniform("u_wipe_colorize_mode", static_cast<int32_t>(wipe_color_mode_));
rhi.set_uniform("u_wipe_encore_swizzle", static_cast<int32_t>(wipe_swizzle_));
rhi.set_sampler("s_sampler0", 0, start_);
rhi.set_sampler("s_sampler1", 1, end_);
rhi.set_sampler("s_sampler2", 2, wipe_tex_);
rhi.bind_vertex_attrib("a_position", vbo_, VertexAttributeFormat::kFloat3, offsetof(PostprocessVertex, x), sizeof(PostprocessVertex));
rhi.bind_vertex_attrib("a_texcoord0", vbo_, VertexAttributeFormat::kFloat2, offsetof(PostprocessVertex, u), sizeof(PostprocessVertex));
rhi.set_viewport({0, 0, width_, height_});
rhi.bind_uniform_set(0, us_);
rhi.bind_binding_set(bs_);
rhi.bind_index_buffer(ibo_);
rhi.draw_indexed(6, 0);
}

View file

@ -21,13 +21,11 @@ namespace srb2::hwr2
class PostprocessWipePass final
{
// Internal RHI resources
rhi::Handle<rhi::Pipeline> pipeline_;
rhi::Handle<rhi::Program> program_;
rhi::Handle<rhi::Buffer> vbo_;
bool upload_vbo_ = false;
rhi::Handle<rhi::Buffer> ibo_;
bool upload_ibo_ = false;
rhi::Handle<rhi::UniformSet> us_;
rhi::Handle<rhi::BindingSet> bs_;
rhi::Handle<rhi::Texture> wipe_tex_;
int wipe_color_mode_ = 0;
int wipe_swizzle_ = 0;

View file

@ -43,78 +43,59 @@ static TwodeePipelineKey pipeline_key_for_cmd(const Draw2dCmd& cmd)
return {hwr2::get_blend_mode(cmd), hwr2::is_draw_lines(cmd)};
}
static PipelineDesc make_pipeline_desc(TwodeePipelineKey key)
static void set_blend_state(RasterizerStateDesc& desc, BlendMode blend)
{
constexpr const VertexInputDesc kTwodeeVertexInput = {
{{sizeof(TwodeeVertex)}},
{{VertexAttributeName::kPosition, 0, 0},
{VertexAttributeName::kTexCoord0, 0, 12},
{VertexAttributeName::kColor, 0, 20}}};
BlendDesc blend_desc;
switch (key.blend)
switch (blend)
{
case BlendMode::kAlphaTransparent:
blend_desc.source_factor_color = BlendFactor::kSourceAlpha;
blend_desc.dest_factor_color = BlendFactor::kOneMinusSourceAlpha;
blend_desc.color_function = BlendFunction::kAdd;
blend_desc.source_factor_alpha = BlendFactor::kOne;
blend_desc.dest_factor_alpha = BlendFactor::kOneMinusSourceAlpha;
blend_desc.alpha_function = BlendFunction::kAdd;
desc.blend_source_factor_color = BlendFactor::kSourceAlpha;
desc.blend_dest_factor_color = BlendFactor::kOneMinusSourceAlpha;
desc.blend_color_function = BlendFunction::kAdd;
desc.blend_source_factor_alpha = BlendFactor::kOne;
desc.blend_dest_factor_alpha = BlendFactor::kOneMinusSourceAlpha;
desc.blend_alpha_function = BlendFunction::kAdd;
break;
case BlendMode::kModulate:
blend_desc.source_factor_color = BlendFactor::kDest;
blend_desc.dest_factor_color = BlendFactor::kZero;
blend_desc.color_function = BlendFunction::kAdd;
blend_desc.source_factor_alpha = BlendFactor::kDestAlpha;
blend_desc.dest_factor_alpha = BlendFactor::kZero;
blend_desc.alpha_function = BlendFunction::kAdd;
desc.blend_source_factor_color = BlendFactor::kDest;
desc.blend_dest_factor_color = BlendFactor::kZero;
desc.blend_color_function = BlendFunction::kAdd;
desc.blend_source_factor_alpha = BlendFactor::kDestAlpha;
desc.blend_dest_factor_alpha = BlendFactor::kZero;
desc.blend_alpha_function = BlendFunction::kAdd;
break;
case BlendMode::kAdditive:
blend_desc.source_factor_color = BlendFactor::kSourceAlpha;
blend_desc.dest_factor_color = BlendFactor::kOne;
blend_desc.color_function = BlendFunction::kAdd;
blend_desc.source_factor_alpha = BlendFactor::kOne;
blend_desc.dest_factor_alpha = BlendFactor::kOneMinusSourceAlpha;
blend_desc.alpha_function = BlendFunction::kAdd;
desc.blend_source_factor_color = BlendFactor::kSourceAlpha;
desc.blend_dest_factor_color = BlendFactor::kOne;
desc.blend_color_function = BlendFunction::kAdd;
desc.blend_source_factor_alpha = BlendFactor::kOne;
desc.blend_dest_factor_alpha = BlendFactor::kOneMinusSourceAlpha;
desc.blend_alpha_function = BlendFunction::kAdd;
break;
case BlendMode::kSubtractive:
blend_desc.source_factor_color = BlendFactor::kSourceAlpha;
blend_desc.dest_factor_color = BlendFactor::kOne;
blend_desc.color_function = BlendFunction::kSubtract;
blend_desc.source_factor_alpha = BlendFactor::kOne;
blend_desc.dest_factor_alpha = BlendFactor::kOneMinusSourceAlpha;
blend_desc.alpha_function = BlendFunction::kAdd;
desc.blend_source_factor_color = BlendFactor::kSourceAlpha;
desc.blend_dest_factor_color = BlendFactor::kOne;
desc.blend_color_function = BlendFunction::kSubtract;
desc.blend_source_factor_alpha = BlendFactor::kOne;
desc.blend_dest_factor_alpha = BlendFactor::kOneMinusSourceAlpha;
desc.blend_alpha_function = BlendFunction::kAdd;
break;
case BlendMode::kReverseSubtractive:
blend_desc.source_factor_color = BlendFactor::kSourceAlpha;
blend_desc.dest_factor_color = BlendFactor::kOne;
blend_desc.color_function = BlendFunction::kReverseSubtract;
blend_desc.source_factor_alpha = BlendFactor::kOne;
blend_desc.dest_factor_alpha = BlendFactor::kOneMinusSourceAlpha;
blend_desc.alpha_function = BlendFunction::kAdd;
desc.blend_source_factor_color = BlendFactor::kSourceAlpha;
desc.blend_dest_factor_color = BlendFactor::kOne;
desc.blend_color_function = BlendFunction::kReverseSubtract;
desc.blend_source_factor_alpha = BlendFactor::kOne;
desc.blend_dest_factor_alpha = BlendFactor::kOneMinusSourceAlpha;
desc.blend_alpha_function = BlendFunction::kAdd;
break;
case BlendMode::kInvertDest:
blend_desc.source_factor_color = BlendFactor::kOne;
blend_desc.dest_factor_color = BlendFactor::kOne;
blend_desc.color_function = BlendFunction::kSubtract;
blend_desc.source_factor_alpha = BlendFactor::kZero;
blend_desc.dest_factor_alpha = BlendFactor::kDestAlpha;
blend_desc.alpha_function = BlendFunction::kAdd;
desc.blend_source_factor_color = BlendFactor::kOne;
desc.blend_dest_factor_color = BlendFactor::kOne;
desc.blend_color_function = BlendFunction::kSubtract;
desc.blend_source_factor_alpha = BlendFactor::kZero;
desc.blend_dest_factor_alpha = BlendFactor::kDestAlpha;
desc.blend_alpha_function = BlendFunction::kAdd;
break;
}
return {
PipelineProgram::kUnshadedPaletted,
kTwodeeVertexInput,
{{{UniformName::kProjection},
{{UniformName::kModelView, UniformName::kTexCoord0Transform, UniformName::kSampler0IsIndexedAlpha}}}},
{{SamplerName::kSampler0, SamplerName::kSampler1, SamplerName::kSampler2}},
std::nullopt,
{blend_desc, {true, true, true, true}},
key.lines ? PrimitiveType::kLines : PrimitiveType::kTriangles,
CullMode::kNone,
FaceWinding::kCounterClockwise,
{0.f, 0.f, 0.f, 1.f}};
}
void TwodeeRenderer::rewrite_patch_quad_vertices(Draw2dList& list, const Draw2dPatchQuad& cmd) const
@ -233,32 +214,16 @@ void TwodeeRenderer::rewrite_patch_quad_vertices(Draw2dList& list, const Draw2dP
void TwodeeRenderer::initialize(Rhi& rhi)
{
{
TwodeePipelineKey alpha_transparent_tris = {BlendMode::kAlphaTransparent, false};
TwodeePipelineKey modulate_tris = {BlendMode::kModulate, false};
TwodeePipelineKey additive_tris = {BlendMode::kAdditive, false};
TwodeePipelineKey subtractive_tris = {BlendMode::kSubtractive, false};
TwodeePipelineKey revsubtractive_tris = {BlendMode::kReverseSubtractive, false};
TwodeePipelineKey invertdest_tris = {BlendMode::kInvertDest, false};
TwodeePipelineKey alpha_transparent_lines = {BlendMode::kAlphaTransparent, true};
TwodeePipelineKey modulate_lines = {BlendMode::kModulate, true};
TwodeePipelineKey additive_lines = {BlendMode::kAdditive, true};
TwodeePipelineKey subtractive_lines = {BlendMode::kSubtractive, true};
TwodeePipelineKey revsubtractive_lines = {BlendMode::kReverseSubtractive, true};
TwodeePipelineKey invertdest_lines = {BlendMode::kInvertDest, true};
pipelines_.insert({alpha_transparent_tris, rhi.create_pipeline(make_pipeline_desc(alpha_transparent_tris))});
pipelines_.insert({modulate_tris, rhi.create_pipeline(make_pipeline_desc(modulate_tris))});
pipelines_.insert({additive_tris, rhi.create_pipeline(make_pipeline_desc(additive_tris))});
pipelines_.insert({subtractive_tris, rhi.create_pipeline(make_pipeline_desc(subtractive_tris))});
pipelines_.insert({revsubtractive_tris, rhi.create_pipeline(make_pipeline_desc(revsubtractive_tris))});
pipelines_.insert({invertdest_tris, rhi.create_pipeline(make_pipeline_desc(invertdest_tris))});
pipelines_.insert({alpha_transparent_lines, rhi.create_pipeline(make_pipeline_desc(alpha_transparent_lines))});
pipelines_.insert({modulate_lines, rhi.create_pipeline(make_pipeline_desc(modulate_lines))});
pipelines_.insert({additive_lines, rhi.create_pipeline(make_pipeline_desc(additive_lines))});
pipelines_.insert({subtractive_lines, rhi.create_pipeline(make_pipeline_desc(subtractive_lines))});
pipelines_.insert({revsubtractive_lines, rhi.create_pipeline(make_pipeline_desc(revsubtractive_lines))});
pipelines_.insert({invertdest_lines, rhi.create_pipeline(make_pipeline_desc(revsubtractive_lines))});
}
ProgramDesc prog_desc;
prog_desc.name = "unshadedpaletted";
const char* defines[] = {
"ENABLE_U_SAMPLER0_IS_INDEXED_ALPHA",
"ENABLE_S_SAMPLER2",
"ENABLE_VA_TEXCOORD0",
"ENABLE_VA_COLOR"
};
prog_desc.defines = tcb::make_span(defines);
program_ = rhi.create_program(prog_desc);
{
default_tex_ = rhi.create_texture({
@ -377,6 +342,8 @@ void TwodeeRenderer::flush(Rhi& rhi, Twodee& twodee)
new_cmd.colormap = nullptr;
// safety: a command list is required to have at least 1 command
new_cmd.pipeline_key = pipeline_key_for_cmd(list.cmds[0]);
new_cmd.primitive = new_cmd.pipeline_key.lines ? PrimitiveType::kLines : PrimitiveType::kTriangles;
new_cmd.blend_mode = new_cmd.pipeline_key.blend;
merged_list.cmds.push_back(std::move(new_cmd));
for (auto& cmd : list.cmds)
@ -458,6 +425,8 @@ void TwodeeRenderer::flush(Rhi& rhi, Twodee& twodee)
}};
std::visit(tex_visitor_again, cmd);
the_new_one.pipeline_key = pipeline_key_for_cmd(cmd);
the_new_one.primitive = the_new_one.pipeline_key.lines ? PrimitiveType::kLines : PrimitiveType::kTriangles;
the_new_one.blend_mode = the_new_one.pipeline_key.blend;
merged_list.cmds.push_back(std::move(the_new_one));
}
@ -495,23 +464,18 @@ void TwodeeRenderer::flush(Rhi& rhi, Twodee& twodee)
rhi.update_buffer(merged_list.vbo, 0, vertex_data);
rhi.update_buffer(merged_list.ibo, 0, index_data);
// Update the binding sets for each individual merged command
VertexAttributeBufferBinding vbos[] = {{0, merged_list.vbo}};
for (auto& mcmd : merged_list.cmds)
{
TextureBinding tx[3];
auto tex_visitor = srb2::Overload {
[&](Handle<Texture> texture)
{
tx[0] = {SamplerName::kSampler0, texture};
tx[1] = {SamplerName::kSampler1, palette_tex};
mcmd.texture_handle = texture;
},
[&](const MergedTwodeeCommandFlatTexture& tex)
{
Handle<Texture> th = flat_manager_->find_or_create_indexed(rhi, tex.lump);
SRB2_ASSERT(th != kNullHandle);
tx[0] = {SamplerName::kSampler0, th};
tx[1] = {SamplerName::kSampler1, palette_tex};
mcmd.texture_handle = th;
}};
if (mcmd.texture)
{
@ -519,8 +483,7 @@ void TwodeeRenderer::flush(Rhi& rhi, Twodee& twodee)
}
else
{
tx[0] = {SamplerName::kSampler0, default_tex_};
tx[1] = {SamplerName::kSampler1, palette_tex};
mcmd.texture_handle = default_tex_;
}
const uint8_t* colormap = mcmd.colormap;
@ -530,38 +493,29 @@ void TwodeeRenderer::flush(Rhi& rhi, Twodee& twodee)
colormap_h = palette_manager_->find_or_create_colormap(rhi, colormap);
SRB2_ASSERT(colormap_h != kNullHandle);
}
tx[2] = {SamplerName::kSampler2, colormap_h};
mcmd.binding_set =
rhi.create_binding_set(pipelines_[mcmd.pipeline_key], {tcb::span(vbos), tcb::span(tx)});
mcmd.colormap_handle = colormap_h;
}
ctx_list_itr++;
}
// Uniform sets
std::array<UniformVariant, 1> g1_uniforms = {
// Projection
glm::mat4(
glm::vec4(2.f / vid.width, 0.f, 0.f, 0.f),
glm::vec4(0.f, -2.f / vid.height, 0.f, 0.f),
glm::vec4(0.f, 0.f, 1.f, 0.f),
glm::vec4(-1.f, 1.f, 0.f, 1.f)
),
};
std::array<UniformVariant, 3> g2_uniforms = {
// ModelView
glm::identity<glm::mat4>(),
// Texcoord0 Transform
glm::identity<glm::mat3>(),
// Sampler 0 Is Indexed Alpha (yes, it always is)
static_cast<int32_t>(1)
};
Handle<UniformSet> us_1 = rhi.create_uniform_set({tcb::span(g1_uniforms)});
Handle<UniformSet> us_2 = rhi.create_uniform_set({tcb::span(g2_uniforms)});
// Presumably, we're already in a renderpass when flush is called
rhi.bind_program(program_);
rhi.set_uniform("u_projection", glm::mat4(
glm::vec4(2.f / vid.width, 0.f, 0.f, 0.f),
glm::vec4(0.f, -2.f / vid.height, 0.f, 0.f),
glm::vec4(0.f, 0.f, 1.f, 0.f),
glm::vec4(-1.f, 1.f, 0.f, 1.f)
));
rhi.set_uniform("u_modelview", glm::identity<glm::mat4>());
rhi.set_uniform("u_texcoord0_transform", glm::identity<glm::mat3>());
rhi.set_uniform("u_sampler0_is_indexed_alpha", static_cast<int32_t>(1));
rhi.set_sampler("s_sampler1", 1, palette_tex);
for (auto& list : cmd_lists_)
{
rhi.bind_vertex_attrib("a_position", list.vbo, VertexAttributeFormat::kFloat3, offsetof(TwodeeVertex, x), sizeof(TwodeeVertex));
rhi.bind_vertex_attrib("a_texcoord0", list.vbo, VertexAttributeFormat::kFloat2, offsetof(TwodeeVertex, u), sizeof(TwodeeVertex));
rhi.bind_vertex_attrib("a_color", list.vbo, VertexAttributeFormat::kFloat4, offsetof(TwodeeVertex, r), sizeof(TwodeeVertex));
for (auto& cmd : list.cmds)
{
if (cmd.elements == 0)
@ -570,13 +524,16 @@ void TwodeeRenderer::flush(Rhi& rhi, Twodee& twodee)
// This shouldn't happen, but, just in case...
continue;
}
SRB2_ASSERT(pipelines_.find(cmd.pipeline_key) != pipelines_.end());
Handle<Pipeline> pl = pipelines_[cmd.pipeline_key];
rhi.bind_pipeline(pl);
RasterizerStateDesc desc;
desc.cull = CullMode::kNone;
desc.primitive = cmd.primitive;
desc.blend_enabled = true;
set_blend_state(desc, cmd.blend_mode);
// Set blend and primitives
rhi.set_rasterizer_state(desc);
rhi.set_viewport({0, 0, static_cast<uint32_t>(vid.width), static_cast<uint32_t>(vid.height)});
rhi.bind_uniform_set(0, us_1);
rhi.bind_uniform_set(1, us_2);
rhi.bind_binding_set(cmd.binding_set);
rhi.set_sampler("s_sampler0", 0, cmd.texture_handle);
rhi.set_sampler("s_sampler2", 2, cmd.colormap_handle);
rhi.bind_index_buffer(list.ibo);
rhi.draw_indexed(cmd.elements, cmd.index_offset);
}

View file

@ -11,10 +11,8 @@
#ifndef __SRB2_HWR2_PASS_TWODEE_HPP__
#define __SRB2_HWR2_PASS_TWODEE_HPP__
#include <memory>
#include <optional>
#include <tuple>
#include <unordered_map>
#include <variant>
#include <vector>
@ -65,10 +63,13 @@ struct MergedTwodeeCommandFlatTexture
struct MergedTwodeeCommand
{
using Texture = std::variant<rhi::Handle<rhi::Texture>, MergedTwodeeCommandFlatTexture>;
TwodeePipelineKey pipeline_key = {};
rhi::Handle<rhi::BindingSet> binding_set = {};
rhi::PrimitiveType primitive;
BlendMode blend_mode;
std::optional<Texture> texture;
TwodeePipelineKey pipeline_key;
rhi::Handle<rhi::Texture> texture_handle;
const uint8_t* colormap;
rhi::Handle<rhi::Texture> colormap_handle;
uint32_t index_offset = 0;
uint32_t elements = 0;
};
@ -96,7 +97,7 @@ class TwodeeRenderer final
std::vector<std::tuple<rhi::Handle<rhi::Buffer>, std::size_t>> ibos_;
rhi::Handle<rhi::Texture> output_;
rhi::Handle<rhi::Texture> default_tex_;
std::unordered_map<TwodeePipelineKey, rhi::Handle<rhi::Pipeline>> pipelines_;
rhi::Handle<rhi::Program> program_;
void rewrite_patch_quad_vertices(Draw2dList& list, const Draw2dPatchQuad& cmd) const;

View file

@ -322,6 +322,7 @@ void I_FinishUpdate(void)
g_hw_state.blit_rect->draw(*rhi);
break;
}
rhi->end_render_pass();
postframe_update(*rhi);

File diff suppressed because it is too large Load diff

View file

@ -71,7 +71,7 @@ struct Gl2Platform
virtual ~Gl2Platform();
virtual void present() = 0;
virtual std::tuple<std::vector<std::string>, std::vector<std::string>> find_shader_sources(PipelineProgram program) = 0;
virtual std::tuple<std::vector<std::string>, std::vector<std::string>> find_shader_sources(const char* name) = 0;
virtual Rect get_default_framebuffer_dimensions() = 0;
};
@ -93,26 +93,13 @@ struct Gl2Renderbuffer : public rhi::Renderbuffer
rhi::RenderbufferDesc desc;
};
struct Gl2UniformSet : public rhi::UniformSet
{
std::vector<rhi::UniformVariant> uniforms;
};
struct Gl2BindingSet : public rhi::BindingSet
{
std::vector<rhi::VertexAttributeBufferBinding> vertex_buffer_bindings;
std::unordered_map<rhi::SamplerName, uint32_t> textures {4};
};
struct Gl2Pipeline : public rhi::Pipeline
struct Gl2Program : public rhi::Program
{
uint32_t vertex_shader = 0;
uint32_t fragment_shader = 0;
uint32_t program = 0;
std::unordered_map<rhi::VertexAttributeName, uint32_t> attrib_locations {2};
std::unordered_map<rhi::UniformName, uint32_t> uniform_locations {2};
std::unordered_map<rhi::SamplerName, uint32_t> sampler_locations {2};
rhi::PipelineDesc desc;
std::unordered_map<std::string, uint32_t> attrib_locations;
std::unordered_map<std::string, uint32_t> uniform_locations;
};
struct Gl2ActiveUniform
@ -130,9 +117,7 @@ class Gl2Rhi final : public Rhi
Slab<Gl2Texture> texture_slab_;
Slab<Gl2Buffer> buffer_slab_;
Slab<Gl2Renderbuffer> renderbuffer_slab_;
Slab<Gl2Pipeline> pipeline_slab_;
Slab<Gl2UniformSet> uniform_set_slab_;
Slab<Gl2BindingSet> binding_set_slab_;
Slab<Gl2Program> program_slab_;
Handle<Buffer> current_index_buffer_;
@ -143,7 +128,7 @@ class Gl2Rhi final : public Rhi
};
using RenderPassState = std::variant<DefaultRenderPassState, RenderPassBeginInfo>;
std::optional<RenderPassState> current_render_pass_;
std::optional<Handle<Pipeline>> current_pipeline_;
std::optional<Handle<Program>> current_program_;
PrimitiveType current_primitive_type_ = PrimitiveType::kPoints;
uint32_t index_buffer_offset_ = 0;
@ -153,13 +138,15 @@ class Gl2Rhi final : public Rhi
uint8_t stencil_back_reference_ = 0;
uint8_t stencil_back_compare_mask_ = 0xFF;
uint8_t stencil_back_write_mask_ = 0xFF;
CompareFunc stencil_front_func_;
CompareFunc stencil_back_func_;
public:
Gl2Rhi(std::unique_ptr<Gl2Platform>&& platform, GlLoadFunc load_func);
virtual ~Gl2Rhi();
virtual Handle<Pipeline> create_pipeline(const PipelineDesc& desc) override;
virtual void destroy_pipeline(Handle<Pipeline> handle) override;
virtual Handle<Program> create_program(const ProgramDesc& desc) override;
virtual void destroy_program(Handle<Program> handle) override;
virtual Handle<Texture> create_texture(const TextureDesc& desc) override;
virtual void destroy_texture(Handle<Texture> handle) override;
@ -190,21 +177,33 @@ public:
TextureFilterMode min,
TextureFilterMode mag
) override;
virtual Handle<UniformSet>
create_uniform_set(const CreateUniformSetInfo& info) override;
virtual Handle<BindingSet>
create_binding_set(Handle<Pipeline> pipeline, const CreateBindingSetInfo& info)
override;
// Graphics context functions
virtual void begin_default_render_pass(bool clear) override;
virtual void begin_render_pass(const RenderPassBeginInfo& info) override;
virtual void end_render_pass() override;
virtual void bind_pipeline(Handle<Pipeline> pipeline) override;
virtual void bind_uniform_set(uint32_t slot, Handle<UniformSet> set) override;
virtual void bind_binding_set(Handle<BindingSet> set) override;
virtual void bind_program(Handle<Program> program) override;
virtual void bind_vertex_attrib(
const char* name,
Handle<Buffer> buffer,
VertexAttributeFormat format,
uint32_t offset,
uint32_t stride
) override;
virtual void bind_index_buffer(Handle<Buffer> buffer) override;
virtual void set_scissor(const Rect& rect) override;
virtual void set_uniform(const char* name, float value) override;
virtual void set_uniform(const char* name, int value) override;
virtual void set_uniform(const char* name, glm::vec2 value) override;
virtual void set_uniform(const char* name, glm::vec3 value) override;
virtual void set_uniform(const char* name, glm::vec4 value) override;
virtual void set_uniform(const char* name, glm::ivec2 value) override;
virtual void set_uniform(const char* name, glm::ivec3 value) override;
virtual void set_uniform(const char* name, glm::ivec4 value) override;
virtual void set_uniform(const char* name, glm::mat2 value) override;
virtual void set_uniform(const char* name, glm::mat3 value) override;
virtual void set_uniform(const char* name, glm::mat4 value) override;
virtual void set_sampler(const char* name, uint32_t slot, Handle<Texture> texture) override;
virtual void set_rasterizer_state(const RasterizerStateDesc& desc) override;
virtual void set_viewport(const Rect& rect) override;
virtual void draw(uint32_t vertex_count, uint32_t first_vertex) override;
virtual void draw_indexed(uint32_t index_count, uint32_t first_index) override;

View file

@ -24,6 +24,7 @@
#include <tcb/span.hpp>
#include "../core/static_vec.hpp"
#include "glm/ext/vector_float2.hpp"
#include "handle.hpp"
namespace srb2::rhi
@ -38,7 +39,8 @@ struct Texture
{
};
struct Pipeline
/// @brief A linked rendering pipeline program combining a vertex shader and fragment shader.
struct Program
{
};
@ -397,16 +399,6 @@ struct ColorMask
bool a;
};
struct BlendDesc
{
BlendFactor source_factor_color;
BlendFactor dest_factor_color;
BlendFunction color_function;
BlendFactor source_factor_alpha;
BlendFactor dest_factor_alpha;
BlendFunction alpha_function;
};
enum class StencilOp
{
kKeep,
@ -419,42 +411,41 @@ enum class StencilOp
kDecrementWrap
};
struct PipelineStencilOpStateDesc
struct ProgramDesc
{
StencilOp fail;
StencilOp pass;
StencilOp depth_fail;
CompareFunc stencil_compare;
const char* name;
tcb::span<const char*> defines;
};
struct PipelineDepthStencilStateDesc
struct RasterizerStateDesc
{
bool depth_test;
bool depth_write;
CompareFunc depth_func;
bool stencil_test;
PipelineStencilOpStateDesc front;
PipelineStencilOpStateDesc back;
};
struct PipelineColorStateDesc
{
std::optional<BlendDesc> blend;
ColorMask color_mask;
};
struct PipelineDesc
{
PipelineProgram program;
VertexInputDesc vertex_input;
UniformInputDesc uniform_input;
SamplerInputDesc sampler_input;
std::optional<PipelineDepthStencilStateDesc> depth_stencil_state;
PipelineColorStateDesc color_state;
PrimitiveType primitive;
CullMode cull;
FaceWinding winding;
glm::vec4 blend_color;
PrimitiveType primitive = PrimitiveType::kTriangles;
CullMode cull = CullMode::kBack;
FaceWinding winding = FaceWinding::kCounterClockwise;
ColorMask color_mask = {true, true, true, true};
bool blend_enabled = false;
glm::vec4 blend_color = {0.0, 0.0, 0.0, 0.0};
BlendFactor blend_source_factor_color = BlendFactor::kOne;
BlendFactor blend_dest_factor_color = BlendFactor::kZero;
BlendFunction blend_color_function = BlendFunction::kAdd;
BlendFactor blend_source_factor_alpha = BlendFactor::kOne;
BlendFactor blend_dest_factor_alpha = BlendFactor::kZero;
BlendFunction blend_alpha_function = BlendFunction::kAdd;
bool depth_test = false;
bool depth_write = true;
CompareFunc depth_func = CompareFunc::kLess;
bool stencil_test = false;
StencilOp front_fail = StencilOp::kKeep;
StencilOp front_pass = StencilOp::kKeep;
StencilOp front_depth_fail = StencilOp::kKeep;
CompareFunc front_stencil_compare = CompareFunc::kAlways;
StencilOp back_fail = StencilOp::kKeep;
StencilOp back_pass = StencilOp::kKeep;
StencilOp back_depth_fail = StencilOp::kKeep;
CompareFunc back_stencil_compare = CompareFunc::kAlways;
bool scissor_test = false;
Rect scissor = {};
};
struct RenderbufferDesc
@ -565,13 +556,6 @@ struct CreateBindingSetInfo
tcb::span<TextureBinding> sampler_textures;
};
struct UniformSet
{
};
struct BindingSet
{
};
struct TextureDetails
{
uint32_t width;
@ -588,8 +572,8 @@ struct Rhi
{
virtual ~Rhi();
virtual Handle<Pipeline> create_pipeline(const PipelineDesc& desc) = 0;
virtual void destroy_pipeline(Handle<Pipeline> handle) = 0;
virtual Handle<Program> create_program(const ProgramDesc& desc) = 0;
virtual void destroy_program(Handle<Program> handle) = 0;
virtual Handle<Texture> create_texture(const TextureDesc& desc) = 0;
virtual void destroy_texture(Handle<Texture> handle) = 0;
@ -620,19 +604,33 @@ struct Rhi
TextureFilterMode min,
TextureFilterMode mag
) = 0;
virtual Handle<UniformSet> create_uniform_set(const CreateUniformSetInfo& info) = 0;
virtual Handle<BindingSet>
create_binding_set(Handle<Pipeline> pipeline, const CreateBindingSetInfo& info) = 0;
// Graphics context functions
virtual void begin_default_render_pass(bool clear) = 0;
virtual void begin_render_pass(const RenderPassBeginInfo& info) = 0;
virtual void end_render_pass() = 0;
virtual void bind_pipeline(Handle<Pipeline> pipeline) = 0;
virtual void bind_uniform_set(uint32_t slot, Handle<UniformSet> set) = 0;
virtual void bind_binding_set(Handle<BindingSet> set) = 0;
virtual void bind_program(Handle<Program> program) = 0;
virtual void bind_vertex_attrib(
const char* name,
Handle<Buffer> buffer,
VertexAttributeFormat format,
uint32_t offset,
uint32_t stride
) = 0;
virtual void bind_index_buffer(Handle<Buffer> buffer) = 0;
virtual void set_scissor(const Rect& rect) = 0;
virtual void set_uniform(const char* name, float value) = 0;
virtual void set_uniform(const char* name, int value) = 0;
virtual void set_uniform(const char* name, glm::vec2 value) = 0;
virtual void set_uniform(const char* name, glm::vec3 value) = 0;
virtual void set_uniform(const char* name, glm::vec4 value) = 0;
virtual void set_uniform(const char* name, glm::ivec2 value) = 0;
virtual void set_uniform(const char* name, glm::ivec3 value) = 0;
virtual void set_uniform(const char* name, glm::ivec4 value) = 0;
virtual void set_uniform(const char* name, glm::mat2 value) = 0;
virtual void set_uniform(const char* name, glm::mat3 value) = 0;
virtual void set_uniform(const char* name, glm::mat4 value) = 0;
virtual void set_sampler(const char* name, uint32_t slot, Handle<Texture> texture) = 0;
virtual void set_rasterizer_state(const RasterizerStateDesc& desc) = 0;
virtual void set_viewport(const Rect& rect) = 0;
virtual void draw(uint32_t vertex_count, uint32_t first_vertex) = 0;
virtual void draw_indexed(uint32_t index_count, uint32_t first_index) = 0;

View file

@ -12,6 +12,7 @@
/// \file
/// \brief SRB2 graphics stuff for SDL
#include <SDL_video.h>
#include <stdlib.h>
#include <errno.h>
#include <memory>

View file

@ -34,35 +34,10 @@ void SdlGl2Platform::present()
SDL_GL_SwapWindow(window);
}
static constexpr const char* pipeline_lump_slug(rhi::PipelineProgram program)
static std::array<std::string, 2> glsllist_lump_names(const char* name)
{
switch (program)
{
case rhi::PipelineProgram::kUnshaded:
return "unshaded";
case rhi::PipelineProgram::kUnshadedPaletted:
return "unshadedpaletted";
case rhi::PipelineProgram::kPostprocessWipe:
return "postprocesswipe";
case rhi::PipelineProgram::kPostimg:
return "postimg";
case rhi::PipelineProgram::kSharpBilinear:
return "sharpbilinear";
case rhi::PipelineProgram::kCrt:
return "crt";
case rhi::PipelineProgram::kCrtSharp:
return "crtsharp";
default:
return "";
}
}
static std::array<std::string, 2> glsllist_lump_names(rhi::PipelineProgram program)
{
const char* pipeline_slug = pipeline_lump_slug(program);
std::string vertex_list_name = fmt::format("rhi_glsllist_{}_vertex", pipeline_slug);
std::string fragment_list_name = fmt::format("rhi_glsllist_{}_fragment", pipeline_slug);
std::string vertex_list_name = fmt::format("rhi_glsllist_{}_vertex", name);
std::string fragment_list_name = fmt::format("rhi_glsllist_{}_fragment", name);
return {std::move(vertex_list_name), std::move(fragment_list_name)};
}
@ -132,9 +107,9 @@ static std::vector<std::string> get_sources_from_glsllist_lump(const char* lumpn
}
std::tuple<std::vector<std::string>, std::vector<std::string>>
SdlGl2Platform::find_shader_sources(rhi::PipelineProgram program)
SdlGl2Platform::find_shader_sources(const char* name)
{
std::array<std::string, 2> glsllist_names = glsllist_lump_names(program);
std::array<std::string, 2> glsllist_names = glsllist_lump_names(name);
std::vector<std::string> vertex_sources = get_sources_from_glsllist_lump(glsllist_names[0].c_str());
std::vector<std::string> fragment_sources = get_sources_from_glsllist_lump(glsllist_names[1].c_str());

View file

@ -27,7 +27,7 @@ struct SdlGl2Platform final : public Gl2Platform
virtual void present() override;
virtual std::tuple<std::vector<std::string>, std::vector<std::string>>
find_shader_sources(PipelineProgram program) override;
find_shader_sources(const char* name) override;
virtual Rect get_default_framebuffer_dimensions() override;
};