Route Shape mesh operations through renderer GL

This commit is contained in:
2026-06-03 07:19:14 +02:00
parent 1ae79ab3c1
commit e1cce05bd6
6 changed files with 1007 additions and 158 deletions

View File

@@ -135,6 +135,36 @@ struct RecordedOpenGlActiveUniformCall {
std::int32_t capacity = 0;
};
struct RecordedOpenGlBufferBindCall {
std::uint32_t target = 0;
std::uint32_t buffer = 0;
};
struct RecordedOpenGlBufferDataCall {
std::uint32_t target = 0;
std::intptr_t byte_count = 0;
const void* data = nullptr;
std::uint32_t usage = 0;
};
struct RecordedOpenGlVertexAttribPointerCall {
std::uint32_t index = 0;
std::int32_t component_count = 0;
std::uint32_t component_type = 0;
std::uint8_t normalized = 0;
std::int32_t stride = 0;
const void* offset = nullptr;
};
struct RecordedOpenGlMeshDrawCall {
bool indexed = false;
std::uint32_t mode = 0;
std::int32_t first = 0;
std::int32_t count = 0;
std::uint32_t index_type = 0;
const void* index_offset = nullptr;
};
std::vector<RecordedOpenGlStateCall> recorded_state_calls;
std::vector<std::uint32_t> recorded_string_queries;
std::vector<pp::renderer::gl::OpenGlDefaultClear> recorded_clear_calls;
@@ -175,11 +205,23 @@ std::vector<std::uint32_t> recorded_program_integer_queries;
std::vector<RecordedOpenGlActiveUniformCall> recorded_active_uniform_calls;
std::vector<std::uint32_t> recorded_uniform_location_programs;
std::vector<std::string_view> recorded_uniform_location_names;
std::vector<std::uint32_t> recorded_generated_buffer_counts;
std::vector<std::uint32_t> recorded_deleted_buffers;
std::vector<std::uint32_t> recorded_generated_vertex_array_counts;
std::vector<std::uint32_t> recorded_deleted_vertex_arrays;
std::vector<RecordedOpenGlBufferBindCall> recorded_buffer_bind_calls;
std::vector<RecordedOpenGlBufferDataCall> recorded_buffer_data_calls;
std::vector<std::uint32_t> recorded_vertex_array_bind_calls;
std::vector<std::uint32_t> recorded_enabled_vertex_attributes;
std::vector<RecordedOpenGlVertexAttribPointerCall> recorded_vertex_attrib_pointer_calls;
std::vector<RecordedOpenGlMeshDrawCall> recorded_mesh_draw_calls;
std::uint32_t next_texture_id = 91U;
std::uint32_t next_framebuffer_id = 44U;
std::uint32_t next_sampler_id = 71U;
std::uint32_t next_shader_id = 301U;
std::uint32_t next_program_id = 401U;
std::uint32_t next_buffer_id = 501U;
std::uint32_t next_vertex_array_id = 601U;
std::int32_t configured_shader_compile_status = 1;
std::int32_t configured_program_link_status = 1;
std::int32_t configured_active_uniform_count = 2;
@@ -792,6 +834,120 @@ std::int32_t record_get_uniform_location(std::uint32_t program, const char* name
return -1;
}
void record_gen_buffers(std::uint32_t count, std::uint32_t* ids) noexcept
{
recorded_generated_buffer_counts.push_back(count);
if (ids == nullptr) {
return;
}
for (std::uint32_t i = 0; i < count; ++i) {
ids[i] = next_buffer_id++;
}
}
void record_delete_buffers(std::uint32_t count, const std::uint32_t* ids) noexcept
{
for (std::uint32_t i = 0; i < count; ++i) {
recorded_deleted_buffers.push_back(ids == nullptr ? 0U : ids[i]);
}
}
void record_bind_buffer(std::uint32_t target, std::uint32_t buffer) noexcept
{
recorded_buffer_bind_calls.push_back(RecordedOpenGlBufferBindCall {
.target = target,
.buffer = buffer,
});
}
void record_buffer_data(
std::uint32_t target,
std::intptr_t byte_count,
const void* data,
std::uint32_t usage) noexcept
{
recorded_buffer_data_calls.push_back(RecordedOpenGlBufferDataCall {
.target = target,
.byte_count = byte_count,
.data = data,
.usage = usage,
});
}
void record_gen_vertex_arrays(std::uint32_t count, std::uint32_t* ids) noexcept
{
recorded_generated_vertex_array_counts.push_back(count);
if (ids == nullptr) {
return;
}
for (std::uint32_t i = 0; i < count; ++i) {
ids[i] = next_vertex_array_id++;
}
}
void record_delete_vertex_arrays(std::uint32_t count, const std::uint32_t* ids) noexcept
{
for (std::uint32_t i = 0; i < count; ++i) {
recorded_deleted_vertex_arrays.push_back(ids == nullptr ? 0U : ids[i]);
}
}
void record_bind_vertex_array(std::uint32_t vertex_array) noexcept
{
recorded_vertex_array_bind_calls.push_back(vertex_array);
}
void record_enable_vertex_attrib_array(std::uint32_t index) noexcept
{
recorded_enabled_vertex_attributes.push_back(index);
}
void record_vertex_attrib_pointer(
std::uint32_t index,
std::int32_t component_count,
std::uint32_t component_type,
std::uint8_t normalized,
std::int32_t stride,
const void* offset) noexcept
{
recorded_vertex_attrib_pointer_calls.push_back(RecordedOpenGlVertexAttribPointerCall {
.index = index,
.component_count = component_count,
.component_type = component_type,
.normalized = normalized,
.stride = stride,
.offset = offset,
});
}
void record_draw_elements(
std::uint32_t mode,
std::int32_t count,
std::uint32_t index_type,
const void* index_offset) noexcept
{
recorded_mesh_draw_calls.push_back(RecordedOpenGlMeshDrawCall {
.indexed = true,
.mode = mode,
.count = count,
.index_type = index_type,
.index_offset = index_offset,
});
}
void record_draw_arrays(
std::uint32_t mode,
std::int32_t first,
std::int32_t count) noexcept
{
recorded_mesh_draw_calls.push_back(RecordedOpenGlMeshDrawCall {
.indexed = false,
.mode = mode,
.first = first,
.count = count,
});
}
void detects_common_extension_capabilities(pp::tests::Harness& h)
{
constexpr std::array<std::string_view, 2> extensions {
@@ -2771,6 +2927,352 @@ void rejects_invalid_uniform_discovery_dispatch(pp::tests::Harness& h)
PP_EXPECT(h, invalid_uniform_name.status().code == pp::foundation::StatusCode::invalid_argument);
}
void creates_indexed_mesh_objects_through_dispatch(pp::tests::Harness& h)
{
recorded_generated_buffer_counts.clear();
recorded_generated_vertex_array_counts.clear();
recorded_buffer_bind_calls.clear();
recorded_buffer_data_calls.clear();
recorded_vertex_array_bind_calls.clear();
recorded_enabled_vertex_attributes.clear();
recorded_vertex_attrib_pointer_calls.clear();
next_buffer_id = 501U;
next_vertex_array_id = 601U;
constexpr std::array<float, 12> vertices {};
constexpr std::array<std::uint16_t, 6> indices { 0, 1, 2, 0, 2, 3 };
constexpr std::array attributes {
pp::renderer::gl::OpenGlVertexAttribute {
.index = 0U,
.component_count = 4,
.component_type = 0x1406U,
.normalized = 0U,
.stride = 32,
.offset = 0U,
},
pp::renderer::gl::OpenGlVertexAttribute {
.index = 1U,
.component_count = 2,
.component_type = 0x1406U,
.normalized = 0U,
.stride = 32,
.offset = 16U,
},
};
const auto mesh = pp::renderer::gl::create_opengl_mesh_objects(
pp::renderer::gl::OpenGlMeshUpload {
.vertex_data = vertices.data(),
.vertex_byte_count = static_cast<std::intptr_t>(sizeof(float) * vertices.size()),
.index_data = indices.data(),
.index_byte_count = static_cast<std::intptr_t>(sizeof(std::uint16_t) * indices.size()),
.indexed = true,
.attributes = attributes,
},
pp::renderer::gl::OpenGlMeshCreateDispatch {
.gen_buffers = record_gen_buffers,
.bind_buffer = record_bind_buffer,
.buffer_data = record_buffer_data,
.gen_vertex_arrays = record_gen_vertex_arrays,
.bind_vertex_array = record_bind_vertex_array,
.enable_vertex_attrib_array = record_enable_vertex_attrib_array,
.vertex_attrib_pointer = record_vertex_attrib_pointer,
});
PP_EXPECT(h, mesh.ok());
PP_EXPECT(h, mesh.value().vertex_buffer == 501U);
PP_EXPECT(h, mesh.value().index_buffer == 502U);
PP_EXPECT(h, mesh.value().vertex_arrays[0] == 601U);
PP_EXPECT(h, mesh.value().vertex_arrays[1] == 602U);
PP_EXPECT(h, recorded_generated_buffer_counts.size() == 1U);
PP_EXPECT(h, recorded_generated_buffer_counts[0] == 2U);
PP_EXPECT(h, recorded_generated_vertex_array_counts.size() == 1U);
PP_EXPECT(h, recorded_generated_vertex_array_counts[0] == 2U);
PP_EXPECT(h, recorded_buffer_data_calls.size() == 2U);
PP_EXPECT(h, recorded_buffer_data_calls[0].target == 0x8893U);
PP_EXPECT(h, recorded_buffer_data_calls[0].byte_count == static_cast<std::intptr_t>(sizeof(std::uint16_t) * indices.size()));
PP_EXPECT(h, recorded_buffer_data_calls[0].data == indices.data());
PP_EXPECT(h, recorded_buffer_data_calls[0].usage == 0x88E4U);
PP_EXPECT(h, recorded_buffer_data_calls[1].target == 0x8892U);
PP_EXPECT(h, recorded_buffer_data_calls[1].byte_count == static_cast<std::intptr_t>(sizeof(float) * vertices.size()));
PP_EXPECT(h, recorded_buffer_data_calls[1].data == vertices.data());
PP_EXPECT(h, recorded_vertex_array_bind_calls.size() == 3U);
PP_EXPECT(h, recorded_vertex_array_bind_calls[0] == 601U);
PP_EXPECT(h, recorded_vertex_array_bind_calls[1] == 602U);
PP_EXPECT(h, recorded_vertex_array_bind_calls[2] == 0U);
PP_EXPECT(h, recorded_enabled_vertex_attributes.size() == 4U);
PP_EXPECT(h, recorded_enabled_vertex_attributes[0] == 0U);
PP_EXPECT(h, recorded_enabled_vertex_attributes[1] == 1U);
PP_EXPECT(h, recorded_enabled_vertex_attributes[2] == 0U);
PP_EXPECT(h, recorded_enabled_vertex_attributes[3] == 1U);
PP_EXPECT(h, recorded_vertex_attrib_pointer_calls.size() == 4U);
PP_EXPECT(h, recorded_vertex_attrib_pointer_calls[0].index == 0U);
PP_EXPECT(h, recorded_vertex_attrib_pointer_calls[0].component_count == 4);
PP_EXPECT(h, recorded_vertex_attrib_pointer_calls[0].component_type == 0x1406U);
PP_EXPECT(h, recorded_vertex_attrib_pointer_calls[1].offset == reinterpret_cast<const void*>(16U));
}
void creates_dynamic_mesh_without_initial_vertex_upload(pp::tests::Harness& h)
{
recorded_generated_buffer_counts.clear();
recorded_buffer_data_calls.clear();
recorded_vertex_array_bind_calls.clear();
next_buffer_id = 701U;
next_vertex_array_id = 801U;
constexpr std::array attributes {
pp::renderer::gl::OpenGlVertexAttribute {
.index = 0U,
.component_count = 4,
.component_type = 0x1406U,
.normalized = 0U,
.stride = 32,
.offset = 0U,
},
};
const auto mesh = pp::renderer::gl::create_opengl_mesh_objects(
pp::renderer::gl::OpenGlMeshUpload {
.vertex_data = nullptr,
.vertex_byte_count = 0,
.index_data = nullptr,
.index_byte_count = 0,
.indexed = false,
.attributes = attributes,
},
pp::renderer::gl::OpenGlMeshCreateDispatch {
.gen_buffers = record_gen_buffers,
.bind_buffer = record_bind_buffer,
.buffer_data = record_buffer_data,
.gen_vertex_arrays = record_gen_vertex_arrays,
.bind_vertex_array = record_bind_vertex_array,
.enable_vertex_attrib_array = record_enable_vertex_attrib_array,
.vertex_attrib_pointer = record_vertex_attrib_pointer,
});
PP_EXPECT(h, mesh.ok());
PP_EXPECT(h, mesh.value().vertex_buffer == 701U);
PP_EXPECT(h, mesh.value().index_buffer == 0U);
PP_EXPECT(h, recorded_generated_buffer_counts.size() == 1U);
PP_EXPECT(h, recorded_generated_buffer_counts[0] == 1U);
PP_EXPECT(h, recorded_buffer_data_calls.empty());
PP_EXPECT(h, recorded_vertex_array_bind_calls.size() == 3U);
}
void updates_draws_and_deletes_mesh_through_dispatch(pp::tests::Harness& h)
{
recorded_buffer_bind_calls.clear();
recorded_buffer_data_calls.clear();
recorded_vertex_array_bind_calls.clear();
recorded_mesh_draw_calls.clear();
recorded_deleted_buffers.clear();
recorded_deleted_vertex_arrays.clear();
constexpr std::array<float, 8> vertices {};
const auto upload_status = pp::renderer::gl::upload_opengl_buffer_data(
pp::renderer::gl::OpenGlBufferUpload {
.target = 0x8892U,
.buffer_id = 501U,
.data = vertices.data(),
.byte_count = static_cast<std::intptr_t>(sizeof(float) * vertices.size()),
.usage = 0x88E4U,
},
pp::renderer::gl::OpenGlBufferUploadDispatch {
.bind_buffer = record_bind_buffer,
.buffer_data = record_buffer_data,
});
const auto indexed_draw = pp::renderer::gl::draw_opengl_mesh(
pp::renderer::gl::OpenGlMeshDraw {
.vertex_array = 601U,
.mode = 0x0004U,
.count = 6,
.indexed = true,
.index_type = 0x1403U,
.index_offset = reinterpret_cast<const void*>(12U),
},
pp::renderer::gl::OpenGlMeshDrawDispatch {
.bind_vertex_array = record_bind_vertex_array,
.draw_elements = record_draw_elements,
.draw_arrays = record_draw_arrays,
});
const auto array_draw = pp::renderer::gl::draw_opengl_mesh(
pp::renderer::gl::OpenGlMeshDraw {
.vertex_array = 602U,
.mode = 0x0001U,
.count = 2,
.indexed = false,
},
pp::renderer::gl::OpenGlMeshDrawDispatch {
.bind_vertex_array = record_bind_vertex_array,
.draw_elements = record_draw_elements,
.draw_arrays = record_draw_arrays,
});
const auto delete_status = pp::renderer::gl::delete_opengl_mesh_objects(
pp::renderer::gl::OpenGlMeshDelete {
.buffers = { 501U, 502U },
.vertex_arrays = { 601U, 602U },
},
pp::renderer::gl::OpenGlMeshDeleteDispatch {
.delete_buffers = record_delete_buffers,
.delete_vertex_arrays = record_delete_vertex_arrays,
});
PP_EXPECT(h, upload_status.ok());
PP_EXPECT(h, indexed_draw.ok());
PP_EXPECT(h, array_draw.ok());
PP_EXPECT(h, delete_status.ok());
PP_EXPECT(h, recorded_buffer_bind_calls.size() >= 2U);
PP_EXPECT(h, recorded_buffer_bind_calls[0].target == 0x8892U);
PP_EXPECT(h, recorded_buffer_bind_calls[0].buffer == 501U);
PP_EXPECT(h, recorded_buffer_bind_calls[1].target == 0x8892U);
PP_EXPECT(h, recorded_buffer_bind_calls[1].buffer == 0U);
PP_EXPECT(h, recorded_buffer_data_calls.size() == 1U);
PP_EXPECT(h, recorded_buffer_data_calls[0].data == vertices.data());
PP_EXPECT(h, recorded_vertex_array_bind_calls.size() == 4U);
PP_EXPECT(h, recorded_vertex_array_bind_calls[0] == 601U);
PP_EXPECT(h, recorded_vertex_array_bind_calls[1] == 0U);
PP_EXPECT(h, recorded_vertex_array_bind_calls[2] == 602U);
PP_EXPECT(h, recorded_vertex_array_bind_calls[3] == 0U);
PP_EXPECT(h, recorded_mesh_draw_calls.size() == 2U);
PP_EXPECT(h, recorded_mesh_draw_calls[0].indexed);
PP_EXPECT(h, recorded_mesh_draw_calls[0].mode == 0x0004U);
PP_EXPECT(h, recorded_mesh_draw_calls[0].count == 6);
PP_EXPECT(h, recorded_mesh_draw_calls[0].index_type == 0x1403U);
PP_EXPECT(h, recorded_mesh_draw_calls[0].index_offset == reinterpret_cast<const void*>(12U));
PP_EXPECT(h, !recorded_mesh_draw_calls[1].indexed);
PP_EXPECT(h, recorded_mesh_draw_calls[1].first == 0);
PP_EXPECT(h, recorded_deleted_buffers.size() == 2U);
PP_EXPECT(h, recorded_deleted_buffers[0] == 501U);
PP_EXPECT(h, recorded_deleted_buffers[1] == 502U);
PP_EXPECT(h, recorded_deleted_vertex_arrays.size() == 2U);
PP_EXPECT(h, recorded_deleted_vertex_arrays[0] == 601U);
PP_EXPECT(h, recorded_deleted_vertex_arrays[1] == 602U);
}
void rejects_invalid_mesh_dispatch(pp::tests::Harness& h)
{
constexpr std::array<float, 4> vertices {};
constexpr std::array attributes {
pp::renderer::gl::OpenGlVertexAttribute {
.index = 0U,
.component_count = 4,
.component_type = 0x1406U,
.normalized = 0U,
.stride = 16,
.offset = 0U,
},
};
constexpr std::array bad_attributes {
pp::renderer::gl::OpenGlVertexAttribute {
.index = 0U,
.component_count = 0,
.component_type = 0x1406U,
.normalized = 0U,
.stride = 16,
.offset = 0U,
},
};
const auto missing_create_dispatch = pp::renderer::gl::create_opengl_mesh_objects(
pp::renderer::gl::OpenGlMeshUpload {
.vertex_data = vertices.data(),
.vertex_byte_count = static_cast<std::intptr_t>(sizeof(float) * vertices.size()),
.attributes = attributes,
},
pp::renderer::gl::OpenGlMeshCreateDispatch {
.gen_buffers = record_gen_buffers,
});
const auto invalid_create_data = pp::renderer::gl::create_opengl_mesh_objects(
pp::renderer::gl::OpenGlMeshUpload {
.vertex_data = nullptr,
.vertex_byte_count = 4,
.attributes = attributes,
},
pp::renderer::gl::OpenGlMeshCreateDispatch {
.gen_buffers = record_gen_buffers,
.bind_buffer = record_bind_buffer,
.buffer_data = record_buffer_data,
.gen_vertex_arrays = record_gen_vertex_arrays,
.bind_vertex_array = record_bind_vertex_array,
.enable_vertex_attrib_array = record_enable_vertex_attrib_array,
.vertex_attrib_pointer = record_vertex_attrib_pointer,
});
const auto invalid_attribute = pp::renderer::gl::create_opengl_mesh_objects(
pp::renderer::gl::OpenGlMeshUpload {
.vertex_data = vertices.data(),
.vertex_byte_count = static_cast<std::intptr_t>(sizeof(float) * vertices.size()),
.attributes = bad_attributes,
},
pp::renderer::gl::OpenGlMeshCreateDispatch {
.gen_buffers = record_gen_buffers,
.bind_buffer = record_bind_buffer,
.buffer_data = record_buffer_data,
.gen_vertex_arrays = record_gen_vertex_arrays,
.bind_vertex_array = record_bind_vertex_array,
.enable_vertex_attrib_array = record_enable_vertex_attrib_array,
.vertex_attrib_pointer = record_vertex_attrib_pointer,
});
const auto missing_upload_dispatch = pp::renderer::gl::upload_opengl_buffer_data(
pp::renderer::gl::OpenGlBufferUpload {
.target = 0x8892U,
.buffer_id = 1U,
.data = vertices.data(),
.byte_count = static_cast<std::intptr_t>(sizeof(float) * vertices.size()),
.usage = 0x88E4U,
},
pp::renderer::gl::OpenGlBufferUploadDispatch {});
const auto invalid_upload = pp::renderer::gl::upload_opengl_buffer_data(
pp::renderer::gl::OpenGlBufferUpload {
.target = 0x8892U,
.buffer_id = 0U,
.data = vertices.data(),
.byte_count = static_cast<std::intptr_t>(sizeof(float) * vertices.size()),
.usage = 0x88E4U,
},
pp::renderer::gl::OpenGlBufferUploadDispatch {
.bind_buffer = record_bind_buffer,
.buffer_data = record_buffer_data,
});
const auto missing_draw_dispatch = pp::renderer::gl::draw_opengl_mesh(
pp::renderer::gl::OpenGlMeshDraw {
.vertex_array = 1U,
.mode = 0x0004U,
.count = 6,
},
pp::renderer::gl::OpenGlMeshDrawDispatch {});
const auto invalid_draw = pp::renderer::gl::draw_opengl_mesh(
pp::renderer::gl::OpenGlMeshDraw {
.vertex_array = 0U,
.mode = 0x0004U,
.count = 6,
},
pp::renderer::gl::OpenGlMeshDrawDispatch {
.bind_vertex_array = record_bind_vertex_array,
.draw_elements = record_draw_elements,
.draw_arrays = record_draw_arrays,
});
const auto missing_delete_dispatch = pp::renderer::gl::delete_opengl_mesh_objects(
pp::renderer::gl::OpenGlMeshDelete {},
pp::renderer::gl::OpenGlMeshDeleteDispatch {});
PP_EXPECT(h, !missing_create_dispatch.ok());
PP_EXPECT(h, missing_create_dispatch.status().code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !invalid_create_data.ok());
PP_EXPECT(h, invalid_create_data.status().code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !invalid_attribute.ok());
PP_EXPECT(h, invalid_attribute.status().code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !missing_upload_dispatch.ok());
PP_EXPECT(h, missing_upload_dispatch.code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !invalid_upload.ok());
PP_EXPECT(h, invalid_upload.code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !missing_draw_dispatch.ok());
PP_EXPECT(h, missing_draw_dispatch.code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !invalid_draw.ok());
PP_EXPECT(h, invalid_draw.code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !missing_delete_dispatch.ok());
PP_EXPECT(h, missing_delete_dispatch.code == pp::foundation::StatusCode::invalid_argument);
}
void updates_texture_2d_through_dispatch(pp::tests::Harness& h)
{
recorded_binding_calls.clear();
@@ -3565,6 +4067,10 @@ int main()
harness.run("rejects_invalid_shader_program_link_dispatch", rejects_invalid_shader_program_link_dispatch);
harness.run("discovers_program_uniforms_through_dispatch", discovers_program_uniforms_through_dispatch);
harness.run("rejects_invalid_uniform_discovery_dispatch", rejects_invalid_uniform_discovery_dispatch);
harness.run("creates_indexed_mesh_objects_through_dispatch", creates_indexed_mesh_objects_through_dispatch);
harness.run("creates_dynamic_mesh_without_initial_vertex_upload", creates_dynamic_mesh_without_initial_vertex_upload);
harness.run("updates_draws_and_deletes_mesh_through_dispatch", updates_draws_and_deletes_mesh_through_dispatch);
harness.run("rejects_invalid_mesh_dispatch", rejects_invalid_mesh_dispatch);
harness.run("updates_texture_2d_through_dispatch", updates_texture_2d_through_dispatch);
harness.run("generates_texture_2d_mipmaps_through_dispatch", generates_texture_2d_mipmaps_through_dispatch);
harness.run("reads_back_texture_2d_through_framebuffer_dispatch", reads_back_texture_2d_through_framebuffer_dispatch);