Bind shared lighting ABI and shade authored local lights
This commit is contained in:
@@ -156,6 +156,15 @@ void SchemaRegistry::validate_component(const Json& component) const {
|
||||
for (const auto& [id, value] : component["fields"].items())
|
||||
if (metadata["fields"].contains(id))
|
||||
validate_field(value, metadata["fields"][id]);
|
||||
if (type == "faset.light") {
|
||||
auto effective = default_fields(type);
|
||||
effective.update(component.at("fields"));
|
||||
if (effective.at("kind") == "spot")
|
||||
require(effective.at("inner_angle").get<double>() <=
|
||||
effective.at("outer_angle").get<double>(),
|
||||
"validation.light_cone",
|
||||
"Spotlight inner_angle must not exceed outer_angle");
|
||||
}
|
||||
}
|
||||
void SchemaRegistry::add_migration(const std::string& type, int from_version, Json rules) {
|
||||
require(contains(type) && from_version > 0 && from_version < schema(type).value("version", 1) &&
|
||||
@@ -267,7 +276,10 @@ SchemaRegistry builtin_schemas() {
|
||||
{"default", 0.7}, {"min", 0.001}, {"max", 1.55},
|
||||
{"unit", "radians"}}},
|
||||
{"casts_shadow", field("boolean", true)},
|
||||
{"shadow_priority", field("integer", 0)}});
|
||||
{"shadow_priority", Json{{"type", "integer"},
|
||||
{"default", 0},
|
||||
{"min", std::numeric_limits<int>::min()},
|
||||
{"max", std::numeric_limits<int>::max()}}}});
|
||||
for (int dimension : {2, 3}) {
|
||||
Json vector = dimension == 2 ? Json{0, 0} : Json{0, 0, 0};
|
||||
Json extents = dimension == 2 ? Json{0.5, 0.5} : Json{0.5, 0.5, 0.5};
|
||||
|
||||
@@ -397,7 +397,9 @@ render::Snapshot SceneView::build(const Json& scene, float aspect, CameraSetting
|
||||
for (const auto coordinate : local.position)
|
||||
if (!std::isfinite(coordinate))
|
||||
invalid("position");
|
||||
local.direction = normalized(direction(model, {0, 0, -1}), id, "direction");
|
||||
if (local.kind == render::LocalLight::Kind::Spot)
|
||||
local.direction = normalized(direction(model, {0, 0, -1}), id,
|
||||
"direction");
|
||||
local.color = color;
|
||||
local.intensity = intensity;
|
||||
local.range = number("range", 10);
|
||||
@@ -405,13 +407,19 @@ render::Snapshot SceneView::build(const Json& scene, float aspect, CameraSetting
|
||||
invalid("range");
|
||||
local.inner_angle = number("inner_angle", 0.35f);
|
||||
local.outer_angle = number("outer_angle", 0.7f);
|
||||
if (local.inner_angle < 0 || local.inner_angle > local.outer_angle ||
|
||||
local.outer_angle <= 0 || local.outer_angle >= std::numbers::pi_v<float> / 2)
|
||||
if (local.kind == render::LocalLight::Kind::Spot &&
|
||||
(local.inner_angle < 0 || local.inner_angle > local.outer_angle ||
|
||||
local.outer_angle <= 0 ||
|
||||
local.outer_angle >= std::numbers::pi_v<float> / 2))
|
||||
invalid("inner_angle/outer_angle");
|
||||
local.casts_shadow = castsShadow;
|
||||
if (fields.contains("shadow_priority")) {
|
||||
if (!fields.at("shadow_priority").is_number_integer())
|
||||
invalid("shadow_priority");
|
||||
const auto priority = fields.at("shadow_priority").get<double>();
|
||||
if (priority < std::numeric_limits<int>::min() ||
|
||||
priority > std::numeric_limits<int>::max())
|
||||
invalid("shadow_priority");
|
||||
local.shadow_priority = fields.at("shadow_priority").get<int>();
|
||||
}
|
||||
out.local_lights.push_back(std::move(local));
|
||||
|
||||
+192
-20
@@ -6,6 +6,7 @@
|
||||
#include <bit>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <faset/core/io.hpp>
|
||||
#include <faset/render/render_graph.hpp>
|
||||
@@ -14,6 +15,7 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <numbers>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
@@ -72,6 +74,40 @@ struct ScenePush {
|
||||
std::array<std::uint32_t, 4> draw_info;
|
||||
};
|
||||
static_assert(sizeof(ScenePush) == 112);
|
||||
struct LightingHeaderGpu {
|
||||
std::array<std::uint32_t, 4> counts{};
|
||||
std::array<float, 4> sun_direction_intensity{};
|
||||
std::array<float, 4> sun_color{};
|
||||
std::array<float, 4> camera_forward_shadow_distance{};
|
||||
std::array<float, 4> cascade_splits{};
|
||||
};
|
||||
struct LocalLightGpu {
|
||||
std::array<float, 4> position_range{};
|
||||
std::array<float, 4> direction_cos_outer{};
|
||||
std::array<float, 4> color_intensity{};
|
||||
std::array<float, 4> cone_type_shadow_view{};
|
||||
std::array<float, 4> reserved{};
|
||||
};
|
||||
struct ShadowViewGpu {
|
||||
Mat4 view_projection{identity};
|
||||
std::array<float, 4> tile_scale_offset{};
|
||||
std::array<float, 4> guarded_clamp{};
|
||||
std::array<float, 4> bias_flags{};
|
||||
};
|
||||
static_assert(sizeof(LightingHeaderGpu) == 80 &&
|
||||
offsetof(LightingHeaderGpu, sun_direction_intensity) == 16 &&
|
||||
offsetof(LightingHeaderGpu, sun_color) == 32 &&
|
||||
offsetof(LightingHeaderGpu, camera_forward_shadow_distance) == 48 &&
|
||||
offsetof(LightingHeaderGpu, cascade_splits) == 64);
|
||||
static_assert(sizeof(LocalLightGpu) == 80 &&
|
||||
offsetof(LocalLightGpu, direction_cos_outer) == 16 &&
|
||||
offsetof(LocalLightGpu, color_intensity) == 32 &&
|
||||
offsetof(LocalLightGpu, cone_type_shadow_view) == 48 &&
|
||||
offsetof(LocalLightGpu, reserved) == 64);
|
||||
static_assert(sizeof(ShadowViewGpu) == 112 &&
|
||||
offsetof(ShadowViewGpu, tile_scale_offset) == 64 &&
|
||||
offsetof(ShadowViewGpu, guarded_clamp) == 80 &&
|
||||
offsetof(ShadowViewGpu, bias_flags) == 96);
|
||||
std::array<float, 4> point(const Mat4& m, std::array<float, 4> p) {
|
||||
std::array<float, 4> o{};
|
||||
for (int r = 0; r < 4; ++r)
|
||||
@@ -196,6 +232,7 @@ struct Renderer::Impl {
|
||||
std::vector<VkImageLayout> swap_layouts;
|
||||
Image color, depth, shadow;
|
||||
Buffer vertices, readback;
|
||||
Buffer lighting_header, lighting_locals, lighting_views;
|
||||
SceneResources scene;
|
||||
InstanceTracker instance_tracker;
|
||||
std::unordered_map<std::string, std::size_t> previous_lods;
|
||||
@@ -212,6 +249,9 @@ struct Renderer::Impl {
|
||||
std::unordered_map<const Texture*, CachedOpacity> opacity_cache;
|
||||
VkDescriptorSetLayout descriptor_layout{};
|
||||
VkDescriptorPool descriptor_pool{};
|
||||
VkDescriptorSetLayout lighting_layout{};
|
||||
VkDescriptorPool lighting_pool{};
|
||||
VkDescriptorSet lighting_set{};
|
||||
VkSampler shadow_sampler{}, color_sampler{};
|
||||
VkPipelineLayout pipeline_layout{};
|
||||
VkPipeline pipeline{}, ui_pipeline{}, shadow_pipeline{}, sprite_pipeline{};
|
||||
@@ -316,6 +356,9 @@ struct Renderer::Impl {
|
||||
destroy(scene.hzb[1]);
|
||||
destroy(vertices);
|
||||
destroy(readback);
|
||||
destroy(lighting_header);
|
||||
destroy(lighting_locals);
|
||||
destroy(lighting_views);
|
||||
destroy(color);
|
||||
destroy(depth);
|
||||
destroy(shadow);
|
||||
@@ -333,8 +376,12 @@ struct Renderer::Impl {
|
||||
vkDestroyPipelineLayout(device, pipeline_layout, nullptr);
|
||||
if (descriptor_pool)
|
||||
vkDestroyDescriptorPool(device, descriptor_pool, nullptr);
|
||||
if (lighting_pool)
|
||||
vkDestroyDescriptorPool(device, lighting_pool, nullptr);
|
||||
if (descriptor_layout)
|
||||
vkDestroyDescriptorSetLayout(device, descriptor_layout, nullptr);
|
||||
if (lighting_layout)
|
||||
vkDestroyDescriptorSetLayout(device, lighting_layout, nullptr);
|
||||
if (shadow_sampler)
|
||||
vkDestroySampler(device, shadow_sampler, nullptr);
|
||||
if (color_sampler)
|
||||
@@ -889,6 +936,31 @@ struct Renderer::Impl {
|
||||
pi.pPoolSizes = sizes;
|
||||
check(vkCreateDescriptorPool(device, &pi, nullptr, &descriptor_pool),
|
||||
"Create descriptor pool");
|
||||
std::array<VkDescriptorSetLayoutBinding, 4> lighting_bindings{};
|
||||
for (std::uint32_t i = 0; i < lighting_bindings.size(); ++i)
|
||||
lighting_bindings[i] = {i,
|
||||
i == 3 ? VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE
|
||||
: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr};
|
||||
li.bindingCount = static_cast<std::uint32_t>(lighting_bindings.size());
|
||||
li.pBindings = lighting_bindings.data();
|
||||
check(vkCreateDescriptorSetLayout(device, &li, nullptr, &lighting_layout),
|
||||
"Create lighting descriptor layout");
|
||||
VkDescriptorPoolSize lighting_sizes[] = {
|
||||
{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3}, {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1}};
|
||||
pi.flags = 0;
|
||||
pi.maxSets = 1;
|
||||
pi.poolSizeCount = 2;
|
||||
pi.pPoolSizes = lighting_sizes;
|
||||
check(vkCreateDescriptorPool(device, &pi, nullptr, &lighting_pool),
|
||||
"Create lighting descriptor pool");
|
||||
VkDescriptorSetAllocateInfo lighting_allocation{};
|
||||
lighting_allocation.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
lighting_allocation.descriptorPool = lighting_pool;
|
||||
lighting_allocation.descriptorSetCount = 1;
|
||||
lighting_allocation.pSetLayouts = &lighting_layout;
|
||||
check(vkAllocateDescriptorSets(device, &lighting_allocation, &lighting_set),
|
||||
"Allocate lighting descriptors");
|
||||
VkSamplerCreateInfo si{};
|
||||
si.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
||||
si.magFilter = si.minFilter = VK_FILTER_NEAREST;
|
||||
@@ -1009,8 +1081,9 @@ struct Renderer::Impl {
|
||||
sizeof(Push)};
|
||||
VkPipelineLayoutCreateInfo li{};
|
||||
li.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||
li.setLayoutCount = 1;
|
||||
li.pSetLayouts = &descriptor_layout;
|
||||
const std::array<VkDescriptorSetLayout, 2> set_layouts{descriptor_layout, lighting_layout};
|
||||
li.setLayoutCount = static_cast<std::uint32_t>(set_layouts.size());
|
||||
li.pSetLayouts = set_layouts.data();
|
||||
li.pushConstantRangeCount = 1;
|
||||
li.pPushConstantRanges = &push;
|
||||
check(vkCreatePipelineLayout(device, &li, nullptr, &pipeline_layout),
|
||||
@@ -1220,14 +1293,14 @@ struct Renderer::Impl {
|
||||
layout.pBindings = hzb.data();
|
||||
check(vkCreateDescriptorSetLayout(device, &layout, nullptr, &scene.hzb_layout),
|
||||
"Create HZB descriptor layout");
|
||||
const std::array<VkDescriptorSetLayout, 2> scene_layouts{descriptor_layout,
|
||||
scene.graphics_layout};
|
||||
const std::array<VkDescriptorSetLayout, 3> scene_layouts{
|
||||
descriptor_layout, lighting_layout, scene.graphics_layout};
|
||||
VkPushConstantRange graphics_push{VK_SHADER_STAGE_VERTEX_BIT |
|
||||
VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
0, sizeof(ScenePush)};
|
||||
VkPipelineLayoutCreateInfo pipeline_info{};
|
||||
pipeline_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||
pipeline_info.setLayoutCount = 2;
|
||||
pipeline_info.setLayoutCount = static_cast<std::uint32_t>(scene_layouts.size());
|
||||
pipeline_info.pSetLayouts = scene_layouts.data();
|
||||
pipeline_info.pushConstantRangeCount = 1;
|
||||
pipeline_info.pPushConstantRanges = &graphics_push;
|
||||
@@ -1533,6 +1606,29 @@ struct Renderer::Impl {
|
||||
VkBufferUsageFlags usage = 0) {
|
||||
upload_scene_buffer(buffer, values.data(), values.size() * sizeof(T), usage);
|
||||
}
|
||||
void update_lighting_descriptors() {
|
||||
const std::array<VkDescriptorBufferInfo, 3> buffers{{
|
||||
{lighting_header.handle, 0, lighting_header.size},
|
||||
{lighting_locals.handle, 0, lighting_locals.size},
|
||||
{lighting_views.handle, 0, lighting_views.size}}};
|
||||
const VkDescriptorImageInfo atlas{VK_NULL_HANDLE, shadow.view,
|
||||
VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL};
|
||||
std::array<VkWriteDescriptorSet, 4> writes{};
|
||||
for (std::uint32_t i = 0; i < writes.size(); ++i) {
|
||||
writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
writes[i].dstSet = lighting_set;
|
||||
writes[i].dstBinding = i;
|
||||
writes[i].descriptorCount = 1;
|
||||
writes[i].descriptorType = i == 3 ? VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE
|
||||
: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
if (i == 3)
|
||||
writes[i].pImageInfo = &atlas;
|
||||
else
|
||||
writes[i].pBufferInfo = &buffers[i];
|
||||
}
|
||||
vkUpdateDescriptorSets(device, static_cast<std::uint32_t>(writes.size()),
|
||||
writes.data(), 0, nullptr);
|
||||
}
|
||||
void update_scene_descriptors(bool occlusion) {
|
||||
auto write_buffers = [&](VkDescriptorSet set, std::span<const Buffer* const> buffers,
|
||||
std::uint32_t first_binding) {
|
||||
@@ -2017,15 +2113,87 @@ struct Renderer::Impl {
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT);
|
||||
update_scene_descriptors(occlusion);
|
||||
}
|
||||
Vec3 direction = snapshot.light_direction;
|
||||
std::optional<SunLight> sun = snapshot.sun;
|
||||
if (!sun && !snapshot.authored_lights_present && snapshot.local_lights.empty())
|
||||
sun = SunLight{"legacy-sun", snapshot.light_direction, {1, 1, 1, 1}, 1, true};
|
||||
Vec3 direction = sun ? sun->direction : snapshot.light_direction;
|
||||
float length = std::sqrt(direction[0] * direction[0] + direction[1] * direction[1] +
|
||||
direction[2] * direction[2]);
|
||||
if (length < 1e-5f) {
|
||||
if (!std::isfinite(length) || length < 1e-5f) {
|
||||
if (sun && sun->stable_id != "legacy-sun")
|
||||
throw std::invalid_argument("Authored sun direction must be finite and nonzero");
|
||||
direction = {-.5f, -1, -.3f};
|
||||
length = std::sqrt(1.34f);
|
||||
}
|
||||
for (auto& v : direction)
|
||||
v /= length;
|
||||
LightingHeaderGpu lighting{};
|
||||
lighting.counts[1] = sun ? 1u : 0u;
|
||||
lighting.counts[2] = sun && sun->casts_shadow ? 1u : 0u;
|
||||
lighting.sun_direction_intensity = {direction[0], direction[1], direction[2],
|
||||
sun ? sun->intensity : 0};
|
||||
lighting.sun_color = sun ? sun->color : Color{0, 0, 0, 1};
|
||||
if (sun && (!std::isfinite(sun->intensity) || sun->intensity < 0 ||
|
||||
std::any_of(sun->color.begin(), sun->color.end(),
|
||||
[](float v) { return !std::isfinite(v) || v < 0; })))
|
||||
throw std::invalid_argument("Authored sun radiance must be finite and nonnegative");
|
||||
lighting.camera_forward_shadow_distance = {0, 0, -1, 80};
|
||||
if (snapshot.camera_frustum) {
|
||||
const auto& view = snapshot.camera_frustum->view;
|
||||
lighting.camera_forward_shadow_distance = {-view[2], -view[6], -view[10], 80};
|
||||
}
|
||||
auto sorted_lights = snapshot.local_lights;
|
||||
std::stable_sort(sorted_lights.begin(), sorted_lights.end(),
|
||||
[](const auto& a, const auto& b) { return a.stable_id < b.stable_id; });
|
||||
constexpr std::size_t max_local_lights = 128;
|
||||
std::vector<LocalLightGpu> gpu_lights;
|
||||
gpu_lights.reserve(std::min(sorted_lights.size(), max_local_lights));
|
||||
for (const auto& local : sorted_lights) {
|
||||
if (gpu_lights.size() == max_local_lights)
|
||||
break;
|
||||
const auto finite_color = std::all_of(local.color.begin(), local.color.end(),
|
||||
[](float v) { return std::isfinite(v) && v >= 0; });
|
||||
const auto finite_position = std::all_of(local.position.begin(), local.position.end(),
|
||||
[](float v) { return std::isfinite(v); });
|
||||
if (!finite_color || !finite_position || !std::isfinite(local.intensity) ||
|
||||
local.intensity < 0 || !std::isfinite(local.range) || local.range <= 0)
|
||||
throw std::invalid_argument("Local light radiance, position and range must be finite");
|
||||
if (local.kind == LocalLight::Kind::Spot &&
|
||||
(!std::isfinite(local.inner_angle) || !std::isfinite(local.outer_angle) ||
|
||||
local.inner_angle < 0 || local.inner_angle > local.outer_angle ||
|
||||
local.outer_angle >= std::numbers::pi_v<float> / 2))
|
||||
throw std::invalid_argument("Spotlight cone angles are invalid");
|
||||
auto spot_direction = local.direction;
|
||||
float spot_length = std::hypot(spot_direction[0], spot_direction[1],
|
||||
spot_direction[2]);
|
||||
if (!std::isfinite(spot_length) || spot_length < 1e-6f) {
|
||||
if (local.kind == LocalLight::Kind::Spot)
|
||||
throw std::invalid_argument("Spotlight direction must be finite and nonzero");
|
||||
spot_direction = {0, 0, -1};
|
||||
spot_length = 1;
|
||||
}
|
||||
for (auto& axis : spot_direction)
|
||||
axis /= spot_length;
|
||||
LocalLightGpu gpu{};
|
||||
gpu.position_range = {local.position[0], local.position[1], local.position[2],
|
||||
local.range};
|
||||
const bool spot = local.kind == LocalLight::Kind::Spot;
|
||||
gpu.direction_cos_outer = {spot_direction[0], spot_direction[1], spot_direction[2],
|
||||
spot ? std::cos(local.outer_angle) : 0.f};
|
||||
gpu.color_intensity = {local.color[0], local.color[1], local.color[2],
|
||||
local.intensity};
|
||||
gpu.cone_type_shadow_view = {spot ? std::cos(local.inner_angle) : 1.f,
|
||||
spot ? 1.f : 0.f, -1, 0};
|
||||
gpu_lights.push_back(gpu);
|
||||
}
|
||||
lighting.counts[0] = static_cast<std::uint32_t>(gpu_lights.size());
|
||||
if (gpu_lights.empty())
|
||||
gpu_lights.push_back({}); // Descriptors always point at a full initialized record.
|
||||
const ShadowViewGpu empty_shadow_view{};
|
||||
upload_scene_buffer(lighting_header, &lighting, sizeof(lighting), 0);
|
||||
upload_scene_vector(lighting_locals, gpu_lights);
|
||||
upload_scene_buffer(lighting_views, &empty_shadow_view, sizeof(empty_shadow_view), 0);
|
||||
update_lighting_descriptors();
|
||||
Vec3 light_eye{-direction[0] * 30, -direction[1] * 30, -direction[2] * 30};
|
||||
Vec3 light_up = std::abs(direction[1]) > .98f ? Vec3{0, 0, 1} : Vec3{0, 1, 0};
|
||||
Push push{multiply(orthographic(-20, 20, -20, 20, .1f, 80),
|
||||
@@ -2078,6 +2246,12 @@ struct Renderer::Impl {
|
||||
vkCmdSetViewport(command, 0, 1, &viewport);
|
||||
vkCmdSetScissor(command, 0, 1, &scissor);
|
||||
};
|
||||
auto bind_material = [&](VkDescriptorSet material) {
|
||||
const std::array<VkDescriptorSet, 2> sets{material, lighting_set};
|
||||
vkCmdBindDescriptorSets(command, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout,
|
||||
0, static_cast<std::uint32_t>(sets.size()), sets.data(),
|
||||
0, nullptr);
|
||||
};
|
||||
auto draw_transparent = [&] {
|
||||
if (transparent_batches.empty())
|
||||
return;
|
||||
@@ -2088,8 +2262,7 @@ struct Renderer::Impl {
|
||||
0, sizeof(push), &push);
|
||||
for (auto batch : transparent_batches) {
|
||||
auto descriptor = textures.at(batch.texture).descriptor;
|
||||
vkCmdBindDescriptorSets(command, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
pipeline_layout, 0, 1, &descriptor, 0, nullptr);
|
||||
bind_material(descriptor);
|
||||
vkCmdDraw(command, batch.count, 1, batch.first, 0);
|
||||
++statistics.draw_calls;
|
||||
}
|
||||
@@ -2102,8 +2275,7 @@ struct Renderer::Impl {
|
||||
sizeof(push), &push);
|
||||
for (auto batch : sprite_batches) {
|
||||
auto descriptor = textures.at(batch.texture).descriptor;
|
||||
vkCmdBindDescriptorSets(command, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout,
|
||||
0, 1, &descriptor, 0, nullptr);
|
||||
bind_material(descriptor);
|
||||
vkCmdDraw(command, batch.count, 1, batch.first, 0);
|
||||
++statistics.draw_calls;
|
||||
}
|
||||
@@ -2125,8 +2297,7 @@ struct Renderer::Impl {
|
||||
continue;
|
||||
vkCmdSetScissor(command, 0, 1, &scissor);
|
||||
auto descriptor = textures.at(batch.texture).descriptor;
|
||||
vkCmdBindDescriptorSets(command, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout,
|
||||
0, 1, &descriptor, 0, nullptr);
|
||||
bind_material(descriptor);
|
||||
vkCmdDraw(command, batch.count, 1, batch.first, 0);
|
||||
++statistics.draw_calls;
|
||||
}
|
||||
@@ -2313,8 +2484,7 @@ struct Renderer::Impl {
|
||||
vkCmdPushConstants(command, pipeline_layout,
|
||||
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0,
|
||||
sizeof(push), &push);
|
||||
vkCmdBindDescriptorSets(command, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1,
|
||||
&white_descriptor, 0, nullptr);
|
||||
bind_material(white_descriptor);
|
||||
if (gpu_active && !gpu_frame.bins.empty()) {
|
||||
VkDeviceSize scene_offset{};
|
||||
vkCmdBindVertexBuffers(command, 0, 1, &scene.vertices.handle, &scene_offset);
|
||||
@@ -2322,7 +2492,7 @@ struct Renderer::Impl {
|
||||
scene.graphics_pipeline);
|
||||
for (std::uint32_t bin = 0; bin < gpu_frame.bins.size(); ++bin) {
|
||||
auto descriptor = textures.at(gpu_frame.textures[bin]).descriptor;
|
||||
const std::array<VkDescriptorSet, 2> sets{descriptor,
|
||||
const std::array<VkDescriptorSet, 3> sets{descriptor, lighting_set,
|
||||
scene.graphics_main};
|
||||
vkCmdBindDescriptorSets(command, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
scene.graphics_pipeline_layout, 0, sets.size(),
|
||||
@@ -2346,8 +2516,7 @@ struct Renderer::Impl {
|
||||
}
|
||||
for (auto batch : scene_batches) {
|
||||
auto descriptor = textures.at(batch.texture).descriptor;
|
||||
vkCmdBindDescriptorSets(command, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout,
|
||||
0, 1, &descriptor, 0, nullptr);
|
||||
bind_material(descriptor);
|
||||
vkCmdDraw(command, batch.count, 1, batch.first, 0);
|
||||
++statistics.draw_calls;
|
||||
}
|
||||
@@ -2446,7 +2615,7 @@ struct Renderer::Impl {
|
||||
scene.graphics_pipeline);
|
||||
for (std::uint32_t bin = 0; bin < gpu_frame.bins.size(); ++bin) {
|
||||
auto descriptor = textures.at(gpu_frame.textures[bin]).descriptor;
|
||||
const std::array<VkDescriptorSet, 2> sets{descriptor,
|
||||
const std::array<VkDescriptorSet, 3> sets{descriptor, lighting_set,
|
||||
scene.graphics_post};
|
||||
vkCmdBindDescriptorSets(command, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
scene.graphics_pipeline_layout, 0,
|
||||
@@ -2618,7 +2787,10 @@ struct Renderer::Impl {
|
||||
++statistics.frame;
|
||||
statistics.gpu_allocated_bytes = vertices.allocation_size + readback.allocation_size +
|
||||
color.allocation_size + depth.allocation_size +
|
||||
shadow.allocation_size;
|
||||
shadow.allocation_size +
|
||||
lighting_header.allocation_size +
|
||||
lighting_locals.allocation_size +
|
||||
lighting_views.allocation_size;
|
||||
statistics.texture_count = static_cast<std::uint32_t>(textures.size());
|
||||
for (const auto& [_, texture] : textures)
|
||||
statistics.gpu_allocated_bytes += texture.image.allocation_size;
|
||||
|
||||
@@ -34,13 +34,21 @@ void validate_layout(const Json& layout, std::string_view entry) {
|
||||
const bool fragment = entry == "fragmentMain";
|
||||
require(layout.at("stage") == (fragment ? "fragment" : "vertex"), "shader stage changed");
|
||||
const auto& descriptors = layout.at("descriptors");
|
||||
require(descriptors.is_array() && descriptors.size() == 4, "descriptor count changed");
|
||||
require(descriptors.is_array() && descriptors.size() == 8, "descriptor count changed");
|
||||
for (std::size_t i = 0; i < descriptors.size(); ++i) {
|
||||
const auto& binding = descriptors[i];
|
||||
require(binding.at("set") == 0 && binding.at("binding") == i && binding.at("count") == 1,
|
||||
const auto set = i < 4 ? 0 : 1;
|
||||
const auto slot = i % 4;
|
||||
require(binding.at("set") == set && binding.at("binding") == slot &&
|
||||
binding.at("count") == 1,
|
||||
"descriptor set, binding or array count changed");
|
||||
require(binding.at("type") == (i % 2 ? "sampler" : "sampled_image_2d"),
|
||||
const auto* expected_type = set == 0 ? (slot % 2 ? "sampler" : "sampled_image_2d")
|
||||
: slot == 3 ? "sampled_image_2d" : "storage_buffer";
|
||||
require(binding.at("type") == expected_type,
|
||||
"descriptor type changed");
|
||||
if (set == 1 && slot < 3)
|
||||
require(binding.at("element_stride") == (slot == 2 ? 112 : 80),
|
||||
"lighting storage record stride changed");
|
||||
require(fragment || !binding.at("used").get<bool>(),
|
||||
"vertex texture bindings are unsupported");
|
||||
}
|
||||
@@ -98,7 +106,7 @@ void validate_gpu_layout(const Json& layout, std::string_view entry) {
|
||||
const std::array<int, 3> graphics_strides{224, 4, 208};
|
||||
for (std::size_t i = 0; i < expected_count; ++i) {
|
||||
const auto& binding = descriptors[i];
|
||||
require(binding.at("set") == (graphics ? 1 : 0) && binding.at("binding") == i &&
|
||||
require(binding.at("set") == (graphics ? 2 : 0) && binding.at("binding") == i &&
|
||||
binding.at("count") == 1,
|
||||
"GPU descriptor set, binding or count changed");
|
||||
const int stride = graphics ? graphics_strides[i] : hzb ? 0 : compute_strides[i];
|
||||
|
||||
Reference in New Issue
Block a user