mirror of
https://github.com/simple64/simple64.git
synced 2026-07-11 01:24:00 +02:00
update parallel rdp
This commit is contained in:
@@ -1 +1 @@
|
||||
43bc31642cc70d04adb828a285e68cdbde7110a9
|
||||
a2b0e8503ba525d70c61bccbe59c87d14daf89a6
|
||||
|
||||
@@ -716,7 +716,7 @@ static int normalize_dzpix(int dz)
|
||||
else if (dz == 0)
|
||||
return 1;
|
||||
|
||||
unsigned bit = 31 - leading_zeroes(dz);
|
||||
unsigned bit = 31 - Util::leading_zeroes(dz);
|
||||
return 1 << (bit + 1);
|
||||
}
|
||||
|
||||
@@ -1680,7 +1680,7 @@ void Renderer::submit_span_setup_jobs(Vulkan::CommandBuffer &cmd, bool upscale)
|
||||
cmd.set_buffer_view(1, 0, *instance.gpu.span_info_jobs_view);
|
||||
cmd.set_specialization_constant_mask(3);
|
||||
cmd.set_specialization_constant(0, (upscale ? caps.upscaling : 1) * ImplementationConstants::DefaultWorkgroupSize);
|
||||
cmd.set_specialization_constant(1, upscale ? trailing_zeroes(caps.upscaling) : 0u);
|
||||
cmd.set_specialization_constant(1, upscale ? Util::trailing_zeroes(caps.upscaling) : 0u);
|
||||
|
||||
Vulkan::QueryPoolHandle begin_ts, end_ts;
|
||||
if (caps.timestamp >= 2)
|
||||
@@ -1780,7 +1780,7 @@ void Renderer::submit_rasterization(Vulkan::CommandBuffer &cmd, Vulkan::Buffer &
|
||||
if (caps.timestamp >= 2)
|
||||
start_ts = cmd.write_timestamp(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
|
||||
|
||||
uint32_t scale_log2_bit = (upscaling ? trailing_zeroes(caps.upscaling) : 0u) << RASTERIZATION_UPSCALING_LOG2_BIT_OFFSET;
|
||||
uint32_t scale_log2_bit = (upscaling ? Util::trailing_zeroes(caps.upscaling) : 0u) << RASTERIZATION_UPSCALING_LOG2_BIT_OFFSET;
|
||||
|
||||
for (size_t i = 0; i < stream.static_raster_state_cache.size(); i++)
|
||||
{
|
||||
@@ -1891,7 +1891,7 @@ void Renderer::submit_tile_binning_combined(Vulkan::CommandBuffer &cmd, bool ups
|
||||
if (supports_subgroup_size_control(32, subgroup_size))
|
||||
{
|
||||
cmd.enable_subgroup_size_control(true);
|
||||
cmd.set_subgroup_size_log2(true, 5, trailing_zeroes(subgroup_size));
|
||||
cmd.set_subgroup_size_log2(true, 5, Util::trailing_zeroes(subgroup_size));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2092,7 +2092,7 @@ void Renderer::submit_depth_blend(Vulkan::CommandBuffer &cmd, Vulkan::Buffer &tm
|
||||
cmd.set_specialization_constant(5, Limits::MaxPrimitives);
|
||||
cmd.set_specialization_constant(6, upscaled ? caps.max_width : Limits::MaxWidth);
|
||||
cmd.set_specialization_constant(7, uint32_t(force_write_mask || (!is_host_coherent && !upscaled)) |
|
||||
((upscaled ? trailing_zeroes(caps.upscaling) : 0u) << 1u));
|
||||
((upscaled ? Util::trailing_zeroes(caps.upscaling) : 0u) << 1u));
|
||||
|
||||
if (upscaled)
|
||||
cmd.set_storage_buffer(0, 0, *upscaling_multisampled_rdram);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -484,7 +484,7 @@ Vulkan::ImageHandle VideoInterface::vram_fetch_stage(const Registers ®s, unsi
|
||||
async_cmd->set_specialization_constant_mask(7);
|
||||
async_cmd->set_specialization_constant(0, uint32_t(rdram_size));
|
||||
async_cmd->set_specialization_constant(1, regs.status & (VI_CONTROL_TYPE_MASK | VI_CONTROL_META_AA_BIT));
|
||||
async_cmd->set_specialization_constant(2, trailing_zeroes(scaling_factor));
|
||||
async_cmd->set_specialization_constant(2, Util::trailing_zeroes(scaling_factor));
|
||||
|
||||
async_cmd->push_constants(&push, 0, sizeof(push));
|
||||
async_cmd->dispatch((extract_width + 15) / 16,
|
||||
|
||||
@@ -40,14 +40,18 @@ struct AlignedAllocation
|
||||
static void *operator new(size_t size)
|
||||
{
|
||||
void *ret = ::Util::memalign_alloc(alignof(T), size);
|
||||
#ifdef __EXCEPTIONS
|
||||
if (!ret) throw std::bad_alloc();
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *operator new[](size_t size)
|
||||
{
|
||||
void *ret = ::Util::memalign_alloc(alignof(T), size);
|
||||
#ifdef __EXCEPTIONS
|
||||
if (!ret) throw std::bad_alloc();
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,13 +29,22 @@
|
||||
namespace Util
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
#define leading_zeroes(x) ((x) == 0 ? 32 : __builtin_clz(x))
|
||||
#define trailing_zeroes(x) ((x) == 0 ? 32 : __builtin_ctz(x))
|
||||
#define trailing_ones(x) __builtin_ctz(~uint32_t(x))
|
||||
#define leading_zeroes64(x) ((x) == 0 ? 64 : __builtin_clzll(x))
|
||||
#define trailing_zeroes64(x) ((x) == 0 ? 64 : __builtin_ctzll(x))
|
||||
#define trailing_ones64(x) __builtin_ctzll(~uint64_t(x))
|
||||
#define popcount32(x) __builtin_popcount(x)
|
||||
#define leading_zeroes_(x) ((x) == 0 ? 32 : __builtin_clz(x))
|
||||
#define trailing_zeroes_(x) ((x) == 0 ? 32 : __builtin_ctz(x))
|
||||
#define trailing_ones_(x) __builtin_ctz(~uint32_t(x))
|
||||
#define leading_zeroes64_(x) ((x) == 0 ? 64 : __builtin_clzll(x))
|
||||
#define trailing_zeroes64_(x) ((x) == 0 ? 64 : __builtin_ctzll(x))
|
||||
#define trailing_ones64_(x) __builtin_ctzll(~uint64_t(x))
|
||||
#define popcount32_(x) __builtin_popcount(x)
|
||||
|
||||
static inline uint32_t leading_zeroes(uint32_t x) { return leading_zeroes_(x); }
|
||||
static inline uint32_t trailing_zeroes(uint32_t x) { return trailing_zeroes_(x); }
|
||||
static inline uint32_t trailing_ones(uint32_t x) { return trailing_ones_(x); }
|
||||
static inline uint32_t leading_zeroes64(uint64_t x) { return leading_zeroes64_(x); }
|
||||
static inline uint32_t trailing_zeroes64(uint64_t x) { return trailing_zeroes64_(x); }
|
||||
static inline uint32_t trailing_ones64(uint64_t x) { return trailing_ones64_(x); }
|
||||
static inline uint32_t popcount32(uint32_t x) { return popcount32_(x); }
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
namespace Internal
|
||||
{
|
||||
@@ -81,13 +90,13 @@ static inline uint32_t ctz64(uint64_t x)
|
||||
}
|
||||
}
|
||||
|
||||
#define popcount32(x) ::Util::Internal::popcount32(x)
|
||||
#define leading_zeroes(x) ::Util::Internal::clz(x)
|
||||
#define trailing_zeroes(x) ::Util::Internal::ctz(x)
|
||||
#define trailing_ones(x) ::Util::Internal::ctz(~uint32_t(x))
|
||||
#define leading_zeroes64(x) ::Util::Internal::clz64(x)
|
||||
#define trailing_zeroes64(x) ::Util::Internal::ctz64(x)
|
||||
#define trailing_ones64(x) ::Util::Internal::ctz64(~uint64_t(x))
|
||||
static inline uint32_t leading_zeroes(uint32_t x) { return Internal::clz(x); }
|
||||
static inline uint32_t trailing_zeroes(uint32_t x) { return Internal::ctz(x); }
|
||||
static inline uint32_t trailing_ones(uint32_t x) { return Internal::ctz(~x); }
|
||||
static inline uint32_t leading_zeroes64(uint64_t x) { return Internal::clz64(x); }
|
||||
static inline uint32_t trailing_zeroes64(uint64_t x) { return Internal::ctz64(x); }
|
||||
static inline uint32_t trailing_ones64(uint64_t x) { return Internal::ctz64(~x); }
|
||||
static inline uint32_t popcount32(uint32_t x) { return Internal::popcount32(x); }
|
||||
#else
|
||||
#error "Implement me."
|
||||
#endif
|
||||
|
||||
@@ -107,7 +107,7 @@ int64_t get_current_time_nsecs()
|
||||
return int64_t(double(li.QuadPart) * static_qpc_freq.inv_freq);
|
||||
#else
|
||||
struct timespec ts = {};
|
||||
#ifdef ANDROID
|
||||
#if defined(ANDROID) || defined(__FreeBSD__)
|
||||
constexpr auto timebase = CLOCK_MONOTONIC;
|
||||
#else
|
||||
constexpr auto timebase = CLOCK_MONOTONIC_RAW;
|
||||
@@ -128,4 +128,4 @@ double Timer::end()
|
||||
auto nt = get_current_time_nsecs();
|
||||
return double(nt - t) * 1e-9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -767,7 +767,7 @@ void CommandBuffer::blit_image(const Image &dst, const Image &src,
|
||||
void CommandBuffer::begin_context()
|
||||
{
|
||||
dirty = ~0u;
|
||||
dirty_sets = ~0u;
|
||||
dirty_sets_realloc = ~0u;
|
||||
dirty_vbos = ~0u;
|
||||
current_pipeline = {};
|
||||
current_pipeline_layout = VK_NULL_HANDLE;
|
||||
@@ -1291,9 +1291,9 @@ Pipeline CommandBuffer::build_graphics_pipeline(Device *device, const DeferredPi
|
||||
|
||||
// Depth state
|
||||
VkPipelineDepthStencilStateCreateInfo ds = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };
|
||||
ds.stencilTestEnable = compile.compatible_render_pass->has_stencil(compile.subpass_index) && compile.static_state.state.stencil_test;
|
||||
ds.depthTestEnable = compile.compatible_render_pass->has_depth(compile.subpass_index) && compile.static_state.state.depth_test;
|
||||
ds.depthWriteEnable = compile.compatible_render_pass->has_depth(compile.subpass_index) && compile.static_state.state.depth_write;
|
||||
ds.stencilTestEnable = compile.compatible_render_pass->has_stencil(compile.subpass_index) && compile.static_state.state.stencil_test != 0;
|
||||
ds.depthTestEnable = compile.compatible_render_pass->has_depth(compile.subpass_index) && compile.static_state.state.depth_test != 0;
|
||||
ds.depthWriteEnable = compile.compatible_render_pass->has_depth(compile.subpass_index) && compile.static_state.state.depth_write != 0;
|
||||
|
||||
if (ds.depthTestEnable)
|
||||
ds.depthCompareOp = static_cast<VkCompareOp>(compile.static_state.state.depth_compare);
|
||||
@@ -1391,7 +1391,7 @@ Pipeline CommandBuffer::build_graphics_pipeline(Device *device, const DeferredPi
|
||||
VkPipelineShaderStageRequiredSubgroupSizeCreateInfo subgroup_size_info_task;
|
||||
VkPipelineShaderStageRequiredSubgroupSizeCreateInfo subgroup_size_info_mesh;
|
||||
|
||||
for (unsigned i = 0; i < Util::ecast(ShaderStage::Count); i++)
|
||||
for (int i = 0; i < Util::ecast(ShaderStage::Count); i++)
|
||||
{
|
||||
auto mask = compile.layout->get_resource_layout().spec_constant_mask[i] &
|
||||
get_combined_spec_constant_mask(compile);
|
||||
@@ -1414,7 +1414,7 @@ Pipeline CommandBuffer::build_graphics_pipeline(Device *device, const DeferredPi
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < Util::ecast(ShaderStage::Count); i++)
|
||||
for (int i = 0; i < Util::ecast(ShaderStage::Count); i++)
|
||||
{
|
||||
auto stage = static_cast<ShaderStage>(i);
|
||||
if (compile.program->get_shader(stage))
|
||||
@@ -2089,7 +2089,7 @@ void CommandBuffer::set_program_layout(const PipelineLayout *layout)
|
||||
VK_ASSERT(layout);
|
||||
if (!pipeline_state.layout)
|
||||
{
|
||||
dirty_sets = ~0u;
|
||||
dirty_sets_realloc = ~0u;
|
||||
set_dirty(COMMAND_BUFFER_DIRTY_PUSH_CONSTANTS_BIT);
|
||||
}
|
||||
else if (layout->get_hash() != pipeline_state.layout->get_hash())
|
||||
@@ -2097,11 +2097,20 @@ void CommandBuffer::set_program_layout(const PipelineLayout *layout)
|
||||
auto &new_layout = layout->get_resource_layout();
|
||||
auto &old_layout = pipeline_state.layout->get_resource_layout();
|
||||
|
||||
uint32_t first_invalidated_set_index = VULKAN_NUM_DESCRIPTOR_SETS;
|
||||
uint32_t new_push_set = layout->get_push_set_index();
|
||||
uint32_t old_push_set = pipeline_state.layout->get_push_set_index();
|
||||
if (new_push_set == old_push_set)
|
||||
{
|
||||
new_push_set = UINT32_MAX;
|
||||
old_push_set = UINT32_MAX;
|
||||
}
|
||||
|
||||
// If the push constant layout changes, all descriptor sets
|
||||
// are invalidated.
|
||||
if (new_layout.push_constant_layout_hash != old_layout.push_constant_layout_hash)
|
||||
{
|
||||
dirty_sets = ~0u;
|
||||
first_invalidated_set_index = 0;
|
||||
set_dirty(COMMAND_BUFFER_DIRTY_PUSH_CONSTANTS_BIT);
|
||||
}
|
||||
else
|
||||
@@ -2109,13 +2118,28 @@ void CommandBuffer::set_program_layout(const PipelineLayout *layout)
|
||||
// Find the first set whose descriptor set layout differs.
|
||||
for (unsigned set = 0; set < VULKAN_NUM_DESCRIPTOR_SETS; set++)
|
||||
{
|
||||
if (layout->get_allocator(set) != pipeline_state.layout->get_allocator(set))
|
||||
if (layout->get_allocator(set) != pipeline_state.layout->get_allocator(set) ||
|
||||
set == new_push_set || set == old_push_set)
|
||||
{
|
||||
dirty_sets |= ~((1u << set) - 1);
|
||||
first_invalidated_set_index = set;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (first_invalidated_set_index < VULKAN_NUM_DESCRIPTOR_SETS)
|
||||
{
|
||||
dirty_sets_rebind |= ~((1u << first_invalidated_set_index) - 1u);
|
||||
|
||||
for (unsigned set = first_invalidated_set_index; set < VULKAN_NUM_DESCRIPTOR_SETS; set++)
|
||||
{
|
||||
if (layout->get_allocator(set) != pipeline_state.layout->get_allocator(set) ||
|
||||
set == new_push_set || set == old_push_set)
|
||||
{
|
||||
dirty_sets_realloc |= 1u << set;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pipeline_state.layout = layout;
|
||||
@@ -2241,21 +2265,21 @@ void CommandBuffer::set_uniform_buffer(unsigned set, unsigned binding, const Buf
|
||||
VK_ASSERT(buffer.get_create_info().usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
|
||||
auto &b = bindings.bindings[set][binding];
|
||||
|
||||
if (buffer.get_cookie() == bindings.cookies[set][binding] && b.buffer.range == range)
|
||||
if (buffer.get_cookie() == bindings.cookies[set][binding] && b.buffer.dynamic.range == range)
|
||||
{
|
||||
if (b.dynamic_offset != offset)
|
||||
if (b.buffer.push.offset != offset)
|
||||
{
|
||||
dirty_sets_dynamic |= 1u << set;
|
||||
b.dynamic_offset = offset;
|
||||
dirty_sets_rebind |= 1u << set;
|
||||
b.buffer.push.offset = offset;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
b.buffer = { buffer.get_buffer(), 0, range };
|
||||
b.dynamic_offset = offset;
|
||||
b.buffer.dynamic = { buffer.get_buffer(), 0, range };
|
||||
b.buffer.push = { buffer.get_buffer(), offset, range };
|
||||
bindings.cookies[set][binding] = buffer.get_cookie();
|
||||
bindings.secondary_cookies[set][binding] = 0;
|
||||
dirty_sets |= 1u << set;
|
||||
dirty_sets_realloc |= 1u << set;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2267,14 +2291,14 @@ void CommandBuffer::set_storage_buffer(unsigned set, unsigned binding, const Buf
|
||||
VK_ASSERT(buffer.get_create_info().usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
|
||||
auto &b = bindings.bindings[set][binding];
|
||||
|
||||
if (buffer.get_cookie() == bindings.cookies[set][binding] && b.buffer.offset == offset && b.buffer.range == range)
|
||||
if (buffer.get_cookie() == bindings.cookies[set][binding] && b.buffer.dynamic.offset == offset && b.buffer.dynamic.range == range)
|
||||
return;
|
||||
|
||||
b.buffer = { buffer.get_buffer(), offset, range };
|
||||
b.dynamic_offset = 0;
|
||||
b.buffer.dynamic = { buffer.get_buffer(), offset, range };
|
||||
b.buffer.push = b.buffer.dynamic;
|
||||
bindings.cookies[set][binding] = buffer.get_cookie();
|
||||
bindings.secondary_cookies[set][binding] = 0;
|
||||
dirty_sets |= 1u << set;
|
||||
dirty_sets_realloc |= 1u << set;
|
||||
}
|
||||
|
||||
void CommandBuffer::set_uniform_buffer(unsigned set, unsigned binding, const Buffer &buffer)
|
||||
@@ -2297,7 +2321,7 @@ void CommandBuffer::set_sampler(unsigned set, unsigned binding, const Sampler &s
|
||||
auto &b = bindings.bindings[set][binding];
|
||||
b.image.fp.sampler = sampler.get_sampler();
|
||||
b.image.integer.sampler = sampler.get_sampler();
|
||||
dirty_sets |= 1u << set;
|
||||
dirty_sets_realloc |= 1u << set;
|
||||
bindings.secondary_cookies[set][binding] = sampler.get_cookie();
|
||||
}
|
||||
|
||||
@@ -2311,7 +2335,7 @@ void CommandBuffer::set_buffer_view_common(unsigned set, unsigned binding, const
|
||||
b.buffer_view = view.get_view();
|
||||
bindings.cookies[set][binding] = view.get_cookie();
|
||||
bindings.secondary_cookies[set][binding] = 0;
|
||||
dirty_sets |= 1u << set;
|
||||
dirty_sets_realloc |= 1u << set;
|
||||
}
|
||||
|
||||
void CommandBuffer::set_buffer_view(unsigned set, unsigned binding, const BufferView &view)
|
||||
@@ -2353,7 +2377,7 @@ void CommandBuffer::set_input_attachments(unsigned set, unsigned start_binding)
|
||||
b.image.fp.imageView = view->get_float_view();
|
||||
b.image.integer.imageView = view->get_integer_view();
|
||||
bindings.cookies[set][start_binding + i] = view->get_cookie();
|
||||
dirty_sets |= 1u << set;
|
||||
dirty_sets_realloc |= 1u << set;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2374,14 +2398,14 @@ void CommandBuffer::set_texture(unsigned set, unsigned binding,
|
||||
b.image.integer.imageLayout = layout;
|
||||
b.image.integer.imageView = integer_view;
|
||||
bindings.cookies[set][binding] = cookie;
|
||||
dirty_sets |= 1u << set;
|
||||
dirty_sets_realloc |= 1u << set;
|
||||
}
|
||||
|
||||
void CommandBuffer::set_bindless(unsigned set, VkDescriptorSet desc_set)
|
||||
{
|
||||
VK_ASSERT(set < VULKAN_NUM_DESCRIPTOR_SETS);
|
||||
bindless_sets[set] = desc_set;
|
||||
dirty_sets |= 1u << set;
|
||||
dirty_sets_realloc |= 1u << set;
|
||||
}
|
||||
|
||||
void CommandBuffer::set_texture(unsigned set, unsigned binding, const ImageView &view)
|
||||
@@ -2452,180 +2476,225 @@ void CommandBuffer::set_unorm_storage_texture(unsigned set, unsigned binding, co
|
||||
view.get_image().get_layout(VK_IMAGE_LAYOUT_GENERAL), view.get_cookie() | COOKIE_BIT_UNORM);
|
||||
}
|
||||
|
||||
void CommandBuffer::rebind_descriptor_set(uint32_t set)
|
||||
void CommandBuffer::flush_descriptor_binds(const VkDescriptorSet *sets,
|
||||
uint32_t &first_set, uint32_t &set_count,
|
||||
uint32_t *dynamic_offsets, uint32_t &num_dynamic_offsets)
|
||||
{
|
||||
auto &layout = pipeline_state.layout->get_resource_layout();
|
||||
if (layout.bindless_descriptor_set_mask & (1u << set))
|
||||
{
|
||||
VK_ASSERT(bindless_sets[set]);
|
||||
table.vkCmdBindDescriptorSets(cmd, actual_render_pass ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
|
||||
current_pipeline_layout, set, 1, &bindless_sets[set], 0, nullptr);
|
||||
if (!set_count)
|
||||
return;
|
||||
}
|
||||
|
||||
auto &set_layout = layout.sets[set];
|
||||
uint32_t num_dynamic_offsets = 0;
|
||||
uint32_t dynamic_offsets[VULKAN_NUM_BINDINGS];
|
||||
table.vkCmdBindDescriptorSets(
|
||||
cmd, actual_render_pass ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
|
||||
current_pipeline_layout, first_set, set_count, sets, num_dynamic_offsets, dynamic_offsets);
|
||||
|
||||
// UBOs
|
||||
for_each_bit(set_layout.uniform_buffer_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
VK_ASSERT(num_dynamic_offsets < VULKAN_NUM_BINDINGS);
|
||||
dynamic_offsets[num_dynamic_offsets++] = bindings.bindings[set][binding + i].dynamic_offset;
|
||||
}
|
||||
});
|
||||
|
||||
table.vkCmdBindDescriptorSets(cmd, actual_render_pass ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
|
||||
current_pipeline_layout, set, 1, &allocated_sets[set], num_dynamic_offsets, dynamic_offsets);
|
||||
set_count = 0;
|
||||
num_dynamic_offsets = 0;
|
||||
}
|
||||
|
||||
void CommandBuffer::flush_descriptor_set(uint32_t set)
|
||||
void CommandBuffer::rebind_descriptor_set(uint32_t set, VkDescriptorSet *sets, uint32_t &first_set, uint32_t &set_count,
|
||||
uint32_t *dynamic_offsets, uint32_t &num_dynamic_offsets)
|
||||
{
|
||||
if (set_count == 0)
|
||||
first_set = set;
|
||||
else if (first_set + set_count != set)
|
||||
{
|
||||
flush_descriptor_binds(sets, first_set, set_count, dynamic_offsets, num_dynamic_offsets);
|
||||
first_set = set;
|
||||
}
|
||||
|
||||
auto &layout = pipeline_state.layout->get_resource_layout();
|
||||
if (layout.bindless_descriptor_set_mask & (1u << set))
|
||||
{
|
||||
VK_ASSERT(bindless_sets[set]);
|
||||
table.vkCmdBindDescriptorSets(cmd, actual_render_pass ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
|
||||
current_pipeline_layout, set, 1, &bindless_sets[set], 0, nullptr);
|
||||
sets[set_count++] = bindless_sets[set];
|
||||
return;
|
||||
}
|
||||
|
||||
auto &set_layout = layout.sets[set];
|
||||
uint32_t num_dynamic_offsets = 0;
|
||||
uint32_t dynamic_offsets[VULKAN_NUM_BINDINGS];
|
||||
Hasher h;
|
||||
|
||||
h.u32(set_layout.fp_mask);
|
||||
|
||||
// UBOs
|
||||
for_each_bit(set_layout.uniform_buffer_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
h.u64(bindings.cookies[set][binding + i]);
|
||||
h.u32(bindings.bindings[set][binding + i].buffer.range);
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].buffer.buffer != VK_NULL_HANDLE);
|
||||
|
||||
VK_ASSERT(num_dynamic_offsets < VULKAN_NUM_BINDINGS);
|
||||
dynamic_offsets[num_dynamic_offsets++] = bindings.bindings[set][binding + i].dynamic_offset;
|
||||
VK_ASSERT(num_dynamic_offsets < VULKAN_NUM_DYNAMIC_UBOS);
|
||||
dynamic_offsets[num_dynamic_offsets++] = bindings.bindings[set][binding + i].buffer.push.offset;
|
||||
}
|
||||
});
|
||||
|
||||
sets[set_count++] = allocated_sets[set];
|
||||
}
|
||||
|
||||
void CommandBuffer::validate_descriptor_binds(uint32_t set)
|
||||
{
|
||||
#ifdef VULKAN_DEBUG
|
||||
auto &layout = pipeline_state.layout->get_resource_layout();
|
||||
auto &set_layout = layout.sets[set];
|
||||
|
||||
for_each_bit(set_layout.uniform_buffer_mask,
|
||||
[&](uint32_t binding)
|
||||
{
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].buffer.dynamic.buffer != VK_NULL_HANDLE);
|
||||
});
|
||||
|
||||
// SSBOs
|
||||
for_each_bit(set_layout.storage_buffer_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
h.u64(bindings.cookies[set][binding + i]);
|
||||
h.u32(bindings.bindings[set][binding + i].buffer.offset);
|
||||
h.u32(bindings.bindings[set][binding + i].buffer.range);
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].buffer.buffer != VK_NULL_HANDLE);
|
||||
}
|
||||
});
|
||||
for_each_bit(set_layout.storage_buffer_mask,
|
||||
[&](uint32_t binding)
|
||||
{
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].buffer.dynamic.buffer != VK_NULL_HANDLE);
|
||||
});
|
||||
|
||||
// Texel buffers
|
||||
for_each_bit(set_layout.sampled_texel_buffer_mask | set_layout.storage_texel_buffer_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
h.u64(bindings.cookies[set][binding + i]);
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].buffer_view != VK_NULL_HANDLE);
|
||||
}
|
||||
});
|
||||
for_each_bit(set_layout.sampled_texel_buffer_mask | set_layout.storage_texel_buffer_mask,
|
||||
[&](uint32_t binding)
|
||||
{
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].buffer_view != VK_NULL_HANDLE);
|
||||
});
|
||||
|
||||
// Sampled images
|
||||
for_each_bit(set_layout.sampled_image_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
h.u64(bindings.cookies[set][binding + i]);
|
||||
if ((set_layout.immutable_sampler_mask & (1u << (binding + i))) == 0)
|
||||
{
|
||||
h.u64(bindings.secondary_cookies[set][binding + i]);
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.sampler != VK_NULL_HANDLE);
|
||||
}
|
||||
h.u32(bindings.bindings[set][binding + i].image.fp.imageLayout);
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
|
||||
}
|
||||
});
|
||||
for_each_bit(set_layout.sampled_image_mask,
|
||||
[&](uint32_t binding)
|
||||
{
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
if ((set_layout.immutable_sampler_mask & (1u << (binding + i))) == 0)
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.sampler != VK_NULL_HANDLE);
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
|
||||
}
|
||||
});
|
||||
|
||||
// Separate images
|
||||
for_each_bit(set_layout.separate_image_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
h.u64(bindings.cookies[set][binding + i]);
|
||||
h.u32(bindings.bindings[set][binding + i].image.fp.imageLayout);
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
|
||||
}
|
||||
});
|
||||
for_each_bit(set_layout.separate_image_mask,
|
||||
[&](uint32_t binding)
|
||||
{
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
|
||||
});
|
||||
|
||||
// Separate samplers
|
||||
for_each_bit(set_layout.sampler_mask & ~set_layout.immutable_sampler_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
h.u64(bindings.secondary_cookies[set][binding + i]);
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.sampler != VK_NULL_HANDLE);
|
||||
}
|
||||
});
|
||||
for_each_bit(set_layout.sampler_mask & ~set_layout.immutable_sampler_mask,
|
||||
[&](uint32_t binding)
|
||||
{
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.sampler != VK_NULL_HANDLE);
|
||||
});
|
||||
|
||||
// Storage images
|
||||
for_each_bit(set_layout.storage_image_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
h.u64(bindings.cookies[set][binding + i]);
|
||||
h.u32(bindings.bindings[set][binding + i].image.fp.imageLayout);
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
|
||||
}
|
||||
});
|
||||
for_each_bit(set_layout.storage_image_mask,
|
||||
[&](uint32_t binding)
|
||||
{
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
|
||||
});
|
||||
|
||||
// Input attachments
|
||||
for_each_bit(set_layout.input_attachment_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
h.u64(bindings.cookies[set][binding + i]);
|
||||
h.u32(bindings.bindings[set][binding + i].image.fp.imageLayout);
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
|
||||
}
|
||||
});
|
||||
for_each_bit(set_layout.input_attachment_mask,
|
||||
[&](uint32_t binding)
|
||||
{
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
|
||||
});
|
||||
#else
|
||||
(void)set;
|
||||
#endif
|
||||
}
|
||||
|
||||
Hash hash = h.get();
|
||||
auto allocated = pipeline_state.layout->get_allocator(set)->find(thread_index, hash);
|
||||
void CommandBuffer::push_descriptor_set(uint32_t set)
|
||||
{
|
||||
#ifdef VULKAN_DEBUG
|
||||
validate_descriptor_binds(set);
|
||||
#endif
|
||||
|
||||
// The descriptor set was not successfully cached, rebuild.
|
||||
if (!allocated.second)
|
||||
VkDescriptorUpdateTemplate update_template = pipeline_state.layout->get_update_template(set);
|
||||
VK_ASSERT(update_template);
|
||||
table.vkCmdPushDescriptorSetWithTemplateKHR(
|
||||
cmd, update_template,
|
||||
pipeline_state.layout->get_layout(), set, bindings.bindings[set]);
|
||||
}
|
||||
|
||||
void CommandBuffer::flush_descriptor_set(uint32_t set, VkDescriptorSet *sets,
|
||||
uint32_t &first_set, uint32_t &set_count,
|
||||
uint32_t *dynamic_offsets, uint32_t &num_dynamic_offsets)
|
||||
{
|
||||
if (set_count == 0)
|
||||
first_set = set;
|
||||
else if (first_set + set_count != set)
|
||||
{
|
||||
auto update_template = pipeline_state.layout->get_update_template(set);
|
||||
VK_ASSERT(update_template);
|
||||
table.vkUpdateDescriptorSetWithTemplate(device->get_device(), allocated.first,
|
||||
update_template, bindings.bindings[set]);
|
||||
flush_descriptor_binds(sets, first_set, set_count, dynamic_offsets, num_dynamic_offsets);
|
||||
first_set = set;
|
||||
}
|
||||
|
||||
table.vkCmdBindDescriptorSets(cmd, actual_render_pass ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
|
||||
current_pipeline_layout, set, 1, &allocated.first, num_dynamic_offsets, dynamic_offsets);
|
||||
allocated_sets[set] = allocated.first;
|
||||
auto &layout = pipeline_state.layout->get_resource_layout();
|
||||
if (layout.bindless_descriptor_set_mask & (1u << set))
|
||||
{
|
||||
VK_ASSERT(bindless_sets[set]);
|
||||
sets[set_count++] = bindless_sets[set];
|
||||
return;
|
||||
}
|
||||
|
||||
auto &set_layout = layout.sets[set];
|
||||
|
||||
#ifdef VULKAN_DEBUG
|
||||
validate_descriptor_binds(set);
|
||||
#endif
|
||||
|
||||
// UBOs
|
||||
for_each_bit(set_layout.uniform_buffer_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
dynamic_offsets[num_dynamic_offsets++] = bindings.bindings[set][binding + i].buffer.push.offset;
|
||||
});
|
||||
|
||||
auto vk_set = pipeline_state.layout->get_allocator(set)->request_descriptor_set(thread_index, device->frame_context_index);
|
||||
|
||||
VkDescriptorUpdateTemplate update_template = pipeline_state.layout->get_update_template(set);
|
||||
VK_ASSERT(update_template);
|
||||
table.vkUpdateDescriptorSetWithTemplate(device->get_device(), vk_set, update_template, bindings.bindings[set]);
|
||||
sets[set_count++] = vk_set;
|
||||
allocated_sets[set] = vk_set;
|
||||
}
|
||||
|
||||
void CommandBuffer::flush_descriptor_sets()
|
||||
{
|
||||
auto &layout = pipeline_state.layout->get_resource_layout();
|
||||
|
||||
uint32_t set_update = layout.descriptor_set_mask & dirty_sets;
|
||||
for_each_bit(set_update, [&](uint32_t set) { flush_descriptor_set(set); });
|
||||
dirty_sets &= ~set_update;
|
||||
uint32_t first_set = 0;
|
||||
uint32_t set_count = 0;
|
||||
VkDescriptorSet sets[VULKAN_NUM_DESCRIPTOR_SETS];
|
||||
uint32_t dynamic_offsets[VULKAN_NUM_DYNAMIC_UBOS];
|
||||
uint32_t num_dynamic_offsets = 0;
|
||||
|
||||
// If we update a set, we also bind dynamically.
|
||||
dirty_sets_dynamic &= ~set_update;
|
||||
dirty_sets_rebind |= dirty_sets_realloc;
|
||||
uint32_t set_update_mask = layout.descriptor_set_mask & dirty_sets_rebind;
|
||||
|
||||
// If we only rebound UBOs, we might get away with just rebinding descriptor sets, no hashing and lookup required.
|
||||
uint32_t dynamic_set_update = layout.descriptor_set_mask & dirty_sets_dynamic;
|
||||
for_each_bit(dynamic_set_update, [&](uint32_t set) { rebind_descriptor_set(set); });
|
||||
dirty_sets_dynamic &= ~dynamic_set_update;
|
||||
uint32_t push_set_index = pipeline_state.layout->get_push_set_index();
|
||||
if (push_set_index != UINT32_MAX && (dirty_sets_rebind & (1u << push_set_index)) != 0)
|
||||
{
|
||||
push_descriptor_set(push_set_index);
|
||||
set_update_mask &= ~(1u << push_set_index);
|
||||
}
|
||||
|
||||
for_each_bit(set_update_mask, [&](uint32_t set) {
|
||||
if ((dirty_sets_realloc & (1u << set)) != 0)
|
||||
flush_descriptor_set(set, sets, first_set, set_count, dynamic_offsets, num_dynamic_offsets);
|
||||
else
|
||||
rebind_descriptor_set(set, sets, first_set, set_count, dynamic_offsets, num_dynamic_offsets);
|
||||
});
|
||||
|
||||
dirty_sets_realloc = 0;
|
||||
dirty_sets_rebind = 0;
|
||||
flush_descriptor_binds(sets, first_set, set_count, dynamic_offsets, num_dynamic_offsets);
|
||||
}
|
||||
|
||||
void CommandBuffer::draw(uint32_t vertex_count, uint32_t instance_count, uint32_t first_vertex, uint32_t first_instance)
|
||||
@@ -2967,7 +3036,7 @@ void CommandBuffer::restore_state(const CommandBufferSavedState &state)
|
||||
memcpy(bindings.bindings[i], state.bindings.bindings[i], sizeof(bindings.bindings[i]));
|
||||
memcpy(bindings.cookies[i], state.bindings.cookies[i], sizeof(bindings.cookies[i]));
|
||||
memcpy(bindings.secondary_cookies[i], state.bindings.secondary_cookies[i], sizeof(bindings.secondary_cookies[i]));
|
||||
dirty_sets |= 1u << i;
|
||||
dirty_sets_realloc |= 1u << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -846,8 +846,8 @@ private:
|
||||
VkRect2D scissor = {};
|
||||
|
||||
CommandBufferDirtyFlags dirty = ~0u;
|
||||
uint32_t dirty_sets = 0;
|
||||
uint32_t dirty_sets_dynamic = 0;
|
||||
uint32_t dirty_sets_realloc = 0;
|
||||
uint32_t dirty_sets_rebind = 0;
|
||||
uint32_t dirty_vbos = 0;
|
||||
uint32_t active_vbos = 0;
|
||||
VkPipelineStageFlags2 uses_swapchain_in_stages = 0;
|
||||
@@ -883,8 +883,20 @@ private:
|
||||
bool flush_compute_pipeline(bool synchronous);
|
||||
void flush_descriptor_sets();
|
||||
void begin_graphics();
|
||||
void flush_descriptor_set(uint32_t set);
|
||||
void rebind_descriptor_set(uint32_t set);
|
||||
void flush_descriptor_set(
|
||||
uint32_t set, VkDescriptorSet *sets,
|
||||
uint32_t &first_set, uint32_t &set_count,
|
||||
uint32_t *dynamic_offsets, uint32_t &num_dynamic_offsets);
|
||||
void push_descriptor_set(uint32_t set);
|
||||
void rebind_descriptor_set(
|
||||
uint32_t set, VkDescriptorSet *sets,
|
||||
uint32_t &first_set, uint32_t &set_count,
|
||||
uint32_t *dynamic_offsets, uint32_t &num_dynamic_offsets);
|
||||
void flush_descriptor_binds(const VkDescriptorSet *sets,
|
||||
uint32_t &first_set, uint32_t &set_count,
|
||||
uint32_t *dynamic_offsets, uint32_t &num_dynamic_offsets);
|
||||
void validate_descriptor_binds(uint32_t set);
|
||||
|
||||
void begin_compute();
|
||||
void begin_context();
|
||||
|
||||
|
||||
@@ -203,10 +203,10 @@ PFN_vkGetInstanceProcAddr Context::get_instance_proc_addr()
|
||||
return instance_proc_addr;
|
||||
}
|
||||
|
||||
bool Context::init_loader(PFN_vkGetInstanceProcAddr addr)
|
||||
bool Context::init_loader(PFN_vkGetInstanceProcAddr addr, bool force_reload)
|
||||
{
|
||||
std::lock_guard<std::mutex> holder(loader_init_lock);
|
||||
if (loader_init_once && !addr)
|
||||
if (loader_init_once && !force_reload && !addr)
|
||||
return true;
|
||||
|
||||
if (!addr)
|
||||
@@ -608,12 +608,6 @@ bool Context::create_instance(const char * const *instance_ext, uint32_t instanc
|
||||
ext.supports_surface_capabilities2 = true;
|
||||
}
|
||||
|
||||
if (ext.supports_surface_capabilities2 && has_extension(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME))
|
||||
{
|
||||
instance_exts.push_back(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME);
|
||||
ext.supports_surface_maintenance1 = true;
|
||||
}
|
||||
|
||||
if ((flags & CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT) != 0 &&
|
||||
has_surface_extension &&
|
||||
has_extension(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME))
|
||||
@@ -630,8 +624,6 @@ bool Context::create_instance(const char * const *instance_ext, uint32_t instanc
|
||||
return layer_itr != end(queried_layers);
|
||||
};
|
||||
|
||||
VkValidationFeaturesEXT validation_features = { VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT };
|
||||
|
||||
force_no_validation = Util::get_environment_bool("GRANITE_VULKAN_NO_VALIDATION", false);
|
||||
|
||||
if (!force_no_validation && has_layer("VK_LAYER_KHRONOS_validation"))
|
||||
@@ -644,6 +636,10 @@ bool Context::create_instance(const char * const *instance_ext, uint32_t instanc
|
||||
std::vector<VkExtensionProperties> layer_exts(layer_ext_count);
|
||||
vkEnumerateInstanceExtensionProperties("VK_LAYER_KHRONOS_validation", &layer_ext_count, layer_exts.data());
|
||||
|
||||
#if 0
|
||||
VkValidationFeaturesEXT validation_features = { VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT };
|
||||
|
||||
// Tons of false positives around timeline semaphores atm, so don't bother.
|
||||
if (find_if(begin(layer_exts), end(layer_exts), [](const VkExtensionProperties &e) {
|
||||
return strcmp(e.extensionName, VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME) == 0;
|
||||
}) != end(layer_exts))
|
||||
@@ -657,6 +653,7 @@ bool Context::create_instance(const char * const *instance_ext, uint32_t instanc
|
||||
validation_features.pEnabledValidationFeatures = validation_sync_features;
|
||||
info.pNext = &validation_features;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!ext.supports_debug_utils &&
|
||||
find_if(begin(layer_exts), end(layer_exts), [](const VkExtensionProperties &e) {
|
||||
@@ -669,6 +666,22 @@ bool Context::create_instance(const char * const *instance_ext, uint32_t instanc
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ext.supports_surface_capabilities2 && has_extension(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME))
|
||||
{
|
||||
#ifdef VULKAN_DEBUG
|
||||
// It seems like there are some bugs with EXT_swapchain_maint1 in VVL atm.
|
||||
const bool support_maint1 = force_no_validation;
|
||||
#else
|
||||
constexpr bool support_maint1 = true;
|
||||
#endif
|
||||
|
||||
if (support_maint1)
|
||||
{
|
||||
instance_exts.push_back(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME);
|
||||
ext.supports_surface_maintenance1 = true;
|
||||
}
|
||||
}
|
||||
|
||||
info.enabledExtensionCount = instance_exts.size();
|
||||
info.ppEnabledExtensionNames = instance_exts.empty() ? nullptr : instance_exts.data();
|
||||
info.enabledLayerCount = instance_layers.size();
|
||||
@@ -1302,7 +1315,14 @@ bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface,
|
||||
ADD_CHAIN(ext.storage_8bit_features, 8BIT_STORAGE_FEATURES_KHR);
|
||||
enabled_extensions.push_back(VK_KHR_8BIT_STORAGE_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
if (ext.device_api_core_version >= VK_API_VERSION_1_3)
|
||||
{
|
||||
ADD_CHAIN(ext.vk13_features, VULKAN_1_3_FEATURES);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (has_extension(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME))
|
||||
{
|
||||
ADD_CHAIN(ext.subgroup_size_control_features, SUBGROUP_SIZE_CONTROL_FEATURES_EXT);
|
||||
@@ -1310,9 +1330,6 @@ bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface,
|
||||
}
|
||||
}
|
||||
|
||||
if (ext.device_api_core_version >= VK_API_VERSION_1_3)
|
||||
ADD_CHAIN(ext.vk13_features, VULKAN_1_3_FEATURES);
|
||||
|
||||
if (has_extension(VK_NV_COMPUTE_SHADER_DERIVATIVES_EXTENSION_NAME))
|
||||
{
|
||||
enabled_extensions.push_back(VK_NV_COMPUTE_SHADER_DERIVATIVES_EXTENSION_NAME);
|
||||
@@ -1362,6 +1379,12 @@ bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface,
|
||||
ADD_CHAIN(ext.device_generated_commands_compute_features, DEVICE_GENERATED_COMMANDS_COMPUTE_FEATURES_NV);
|
||||
}
|
||||
|
||||
if (has_extension(VK_NV_DESCRIPTOR_POOL_OVERALLOCATION_EXTENSION_NAME))
|
||||
{
|
||||
enabled_extensions.push_back(VK_NV_DESCRIPTOR_POOL_OVERALLOCATION_EXTENSION_NAME);
|
||||
ADD_CHAIN(ext.descriptor_pool_overallocation_features, DESCRIPTOR_POOL_OVERALLOCATION_FEATURES_NV);
|
||||
}
|
||||
|
||||
if (has_extension(VK_EXT_MESH_SHADER_EXTENSION_NAME))
|
||||
{
|
||||
enabled_extensions.push_back(VK_EXT_MESH_SHADER_EXTENSION_NAME);
|
||||
@@ -1374,6 +1397,12 @@ bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface,
|
||||
ADD_CHAIN(ext.index_type_uint8_features, INDEX_TYPE_UINT8_FEATURES_EXT);
|
||||
}
|
||||
|
||||
if (has_extension(VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME))
|
||||
{
|
||||
enabled_extensions.push_back(VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME);
|
||||
ADD_CHAIN(ext.rgba10x6_formats_features, RGBA10X6_FORMATS_FEATURES_EXT);
|
||||
}
|
||||
|
||||
if (has_extension(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME))
|
||||
{
|
||||
ext.supports_external_memory_host = true;
|
||||
@@ -1398,6 +1427,18 @@ bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface,
|
||||
ext.supports_push_descriptor = true;
|
||||
}
|
||||
|
||||
if (has_extension(VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME))
|
||||
{
|
||||
enabled_extensions.push_back(VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME);
|
||||
ADD_CHAIN(ext.image_compression_control_features, IMAGE_COMPRESSION_CONTROL_FEATURES_EXT);
|
||||
}
|
||||
|
||||
if (has_extension(VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME))
|
||||
{
|
||||
enabled_extensions.push_back(VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME);
|
||||
ADD_CHAIN(ext.image_compression_control_swapchain_features, IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT);
|
||||
}
|
||||
|
||||
if (ext.device_api_core_version >= VK_API_VERSION_1_3)
|
||||
{
|
||||
ext.supports_store_op_none = true;
|
||||
@@ -1581,14 +1622,14 @@ bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface,
|
||||
{
|
||||
if (has_extension(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME))
|
||||
ADD_CHAIN(driver_properties, DRIVER_PROPERTIES);
|
||||
if (has_extension(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME))
|
||||
ADD_CHAIN(size_control_props, SUBGROUP_SIZE_CONTROL_PROPERTIES);
|
||||
ADD_CHAIN(id_properties, ID_PROPERTIES);
|
||||
ADD_CHAIN(subgroup_properties, SUBGROUP_PROPERTIES);
|
||||
}
|
||||
|
||||
if (ext.device_api_core_version >= VK_API_VERSION_1_3)
|
||||
ADD_CHAIN(ext.vk13_props, VULKAN_1_3_PROPERTIES);
|
||||
else if (has_extension(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME))
|
||||
ADD_CHAIN(size_control_props, SUBGROUP_SIZE_CONTROL_PROPERTIES);
|
||||
|
||||
if (ext.supports_external_memory_host)
|
||||
ADD_CHAIN(ext.host_memory_properties, EXTERNAL_MEMORY_HOST_PROPERTIES_EXT);
|
||||
|
||||
@@ -100,12 +100,16 @@ struct DeviceFeatures
|
||||
VkPhysicalDeviceMeshShaderFeaturesEXT mesh_shader_features = {};
|
||||
VkPhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = {};
|
||||
VkPhysicalDeviceIndexTypeUint8FeaturesEXT index_type_uint8_features = {};
|
||||
VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT rgba10x6_formats_features = {};
|
||||
VkPhysicalDeviceImageCompressionControlFeaturesEXT image_compression_control_features = {};
|
||||
VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT image_compression_control_swapchain_features = {};
|
||||
|
||||
// Vendor
|
||||
VkPhysicalDeviceComputeShaderDerivativesFeaturesNV compute_shader_derivative_features = {};
|
||||
VkPhysicalDeviceDeviceGeneratedCommandsFeaturesNV device_generated_commands_features = {};
|
||||
VkPhysicalDeviceDeviceGeneratedCommandsComputeFeaturesNV device_generated_commands_compute_features = {};
|
||||
VkPhysicalDeviceDeviceGeneratedCommandsPropertiesNV device_generated_commands_properties = {};
|
||||
VkPhysicalDeviceDescriptorPoolOverallocationFeaturesNV descriptor_pool_overallocation_features = {};
|
||||
|
||||
// Fallback feature structs (Vulkan 1.1)
|
||||
VkPhysicalDeviceHostQueryResetFeatures host_query_reset_features = {};
|
||||
@@ -239,7 +243,7 @@ public:
|
||||
Context();
|
||||
Context(const Context &) = delete;
|
||||
void operator=(const Context &) = delete;
|
||||
static bool init_loader(PFN_vkGetInstanceProcAddr addr);
|
||||
static bool init_loader(PFN_vkGetInstanceProcAddr addr, bool force_reload = false);
|
||||
static PFN_vkGetInstanceProcAddr get_instance_proc_addr();
|
||||
|
||||
~Context();
|
||||
|
||||
@@ -40,9 +40,8 @@ DescriptorSetAllocator::DescriptorSetAllocator(Hash hash, Device *device_, const
|
||||
|
||||
if (!bindless)
|
||||
{
|
||||
unsigned count = device_->num_thread_indices;
|
||||
for (unsigned i = 0; i < count; i++)
|
||||
per_thread.emplace_back(new PerThread);
|
||||
unsigned count = device_->num_thread_indices * device_->per_frame.size();
|
||||
per_thread_and_frame.resize(count);
|
||||
}
|
||||
|
||||
if (bindless && !device->get_device_features().vk12_features.descriptorIndexing)
|
||||
@@ -176,11 +175,27 @@ DescriptorSetAllocator::DescriptorSetAllocator(Hash hash, Device *device_, const
|
||||
#ifdef VULKAN_DEBUG
|
||||
LOGI("Creating descriptor set layout.\n");
|
||||
#endif
|
||||
if (table.vkCreateDescriptorSetLayout(device->get_device(), &info, nullptr, &set_layout) != VK_SUCCESS)
|
||||
if (table.vkCreateDescriptorSetLayout(device->get_device(), &info, nullptr, &set_layout_pool) != VK_SUCCESS)
|
||||
LOGE("Failed to create descriptor set layout.");
|
||||
|
||||
#ifdef GRANITE_VULKAN_FOSSILIZE
|
||||
device->register_descriptor_set_layout(set_layout, get_hash(), info);
|
||||
if (set_layout_pool)
|
||||
device->register_descriptor_set_layout(set_layout_pool, get_hash(), info);
|
||||
#endif
|
||||
|
||||
if (!bindless && device->get_device_features().supports_push_descriptor)
|
||||
{
|
||||
info.flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;
|
||||
for (auto &b : bindings)
|
||||
if (b.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC)
|
||||
b.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
if (table.vkCreateDescriptorSetLayout(device->get_device(), &info, nullptr, &set_layout_push) != VK_SUCCESS)
|
||||
LOGE("Failed to create descriptor set layout.");
|
||||
#ifdef GRANITE_VULKAN_FOSSILIZE
|
||||
if (set_layout_push)
|
||||
device->register_descriptor_set_layout(set_layout_push, get_hash(), info);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void DescriptorSetAllocator::reset_bindless_pool(VkDescriptorPool pool)
|
||||
@@ -196,7 +211,7 @@ VkDescriptorSet DescriptorSetAllocator::allocate_bindless_set(VkDescriptorPool p
|
||||
VkDescriptorSetAllocateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
|
||||
info.descriptorPool = pool;
|
||||
info.descriptorSetCount = 1;
|
||||
info.pSetLayouts = &set_layout;
|
||||
info.pSetLayouts = &set_layout_pool;
|
||||
|
||||
VkDescriptorSetVariableDescriptorCountAllocateInfoEXT count_info =
|
||||
{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO_EXT };
|
||||
@@ -225,12 +240,6 @@ VkDescriptorPool DescriptorSetAllocator::allocate_bindless_pool(unsigned num_set
|
||||
info.poolSizeCount = 1;
|
||||
|
||||
VkDescriptorPoolSize size = pool_size[0];
|
||||
if (num_descriptors > size.descriptorCount)
|
||||
{
|
||||
LOGE("Trying to allocate more than max bindless descriptors for descriptor layout.\n");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
size.descriptorCount = num_descriptors;
|
||||
info.pPoolSizes = &size;
|
||||
|
||||
@@ -247,82 +256,98 @@ void DescriptorSetAllocator::begin_frame()
|
||||
{
|
||||
if (!bindless)
|
||||
{
|
||||
for (auto &thr : per_thread)
|
||||
thr->should_begin = true;
|
||||
// This can only be called in a situation where no command buffers are alive,
|
||||
// so we don't need to consider any locks here.
|
||||
if (device->per_frame.size() * device->num_thread_indices != per_thread_and_frame.size())
|
||||
per_thread_and_frame.resize(device->per_frame.size() * device->num_thread_indices);
|
||||
|
||||
// It would be safe to set all offsets to 0 here, but that's a little wasteful.
|
||||
for (uint32_t i = 0; i < device->num_thread_indices; i++)
|
||||
per_thread_and_frame[i * device->per_frame.size() + device->frame_context_index].offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<VkDescriptorSet, bool> DescriptorSetAllocator::find(unsigned thread_index, Hash hash)
|
||||
VkDescriptorSet DescriptorSetAllocator::request_descriptor_set(unsigned thread_index, unsigned frame_index)
|
||||
{
|
||||
VK_ASSERT(!bindless);
|
||||
|
||||
auto &state = *per_thread[thread_index];
|
||||
if (state.should_begin)
|
||||
size_t flattened_index = thread_index * device->per_frame.size() + frame_index;
|
||||
|
||||
auto &state = per_thread_and_frame[flattened_index];
|
||||
|
||||
unsigned pool_index = state.offset / VULKAN_NUM_SETS_PER_POOL;
|
||||
unsigned pool_offset = state.offset % VULKAN_NUM_SETS_PER_POOL;
|
||||
|
||||
if (pool_index >= state.pools.size())
|
||||
{
|
||||
state.set_nodes.begin_frame();
|
||||
state.should_begin = false;
|
||||
Pool *pool = state.object_pool.allocate();
|
||||
|
||||
VkDescriptorPoolCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
|
||||
info.maxSets = VULKAN_NUM_SETS_PER_POOL;
|
||||
if (!pool_size.empty())
|
||||
{
|
||||
info.poolSizeCount = pool_size.size();
|
||||
info.pPoolSizes = pool_size.data();
|
||||
}
|
||||
|
||||
bool overallocation =
|
||||
device->get_device_features().descriptor_pool_overallocation_features.descriptorPoolOverallocation ==
|
||||
VK_TRUE;
|
||||
|
||||
if (overallocation)
|
||||
{
|
||||
// No point in allocating new pools if we can keep using the existing one.
|
||||
info.flags |= VK_DESCRIPTOR_POOL_CREATE_ALLOW_OVERALLOCATION_POOLS_BIT_NV |
|
||||
VK_DESCRIPTOR_POOL_CREATE_ALLOW_OVERALLOCATION_SETS_BIT_NV;
|
||||
}
|
||||
|
||||
bool need_alloc = !overallocation || state.pools.empty();
|
||||
|
||||
pool->pool = VK_NULL_HANDLE;
|
||||
if (need_alloc && table.vkCreateDescriptorPool(device->get_device(), &info, nullptr, &pool->pool) != VK_SUCCESS)
|
||||
{
|
||||
LOGE("Failed to create descriptor pool.\n");
|
||||
state.object_pool.free(pool);
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VkDescriptorSetLayout layouts[VULKAN_NUM_SETS_PER_POOL];
|
||||
std::fill(std::begin(layouts), std::end(layouts), set_layout_pool);
|
||||
|
||||
VkDescriptorSetAllocateInfo alloc = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
|
||||
alloc.descriptorPool = pool->pool != VK_NULL_HANDLE ? pool->pool : state.pools.front()->pool;
|
||||
alloc.descriptorSetCount = VULKAN_NUM_SETS_PER_POOL;
|
||||
alloc.pSetLayouts = layouts;
|
||||
|
||||
if (table.vkAllocateDescriptorSets(device->get_device(), &alloc, pool->sets) != VK_SUCCESS)
|
||||
LOGE("Failed to allocate descriptor sets.\n");
|
||||
state.pools.push_back(pool);
|
||||
}
|
||||
|
||||
auto *node = state.set_nodes.request(hash);
|
||||
if (node)
|
||||
return { node->set, true };
|
||||
|
||||
node = state.set_nodes.request_vacant(hash);
|
||||
if (node)
|
||||
return { node->set, false };
|
||||
|
||||
VkDescriptorPool pool;
|
||||
VkDescriptorPoolCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
|
||||
info.maxSets = VULKAN_NUM_SETS_PER_POOL;
|
||||
if (!pool_size.empty())
|
||||
{
|
||||
info.poolSizeCount = pool_size.size();
|
||||
info.pPoolSizes = pool_size.data();
|
||||
}
|
||||
|
||||
if (table.vkCreateDescriptorPool(device->get_device(), &info, nullptr, &pool) != VK_SUCCESS)
|
||||
{
|
||||
LOGE("Failed to create descriptor pool.\n");
|
||||
return { VK_NULL_HANDLE, false };
|
||||
}
|
||||
|
||||
VkDescriptorSet sets[VULKAN_NUM_SETS_PER_POOL];
|
||||
VkDescriptorSetLayout layouts[VULKAN_NUM_SETS_PER_POOL];
|
||||
std::fill(std::begin(layouts), std::end(layouts), set_layout);
|
||||
|
||||
VkDescriptorSetAllocateInfo alloc = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
|
||||
alloc.descriptorPool = pool;
|
||||
alloc.descriptorSetCount = VULKAN_NUM_SETS_PER_POOL;
|
||||
alloc.pSetLayouts = layouts;
|
||||
|
||||
if (table.vkAllocateDescriptorSets(device->get_device(), &alloc, sets) != VK_SUCCESS)
|
||||
LOGE("Failed to allocate descriptor sets.\n");
|
||||
state.pools.push_back(pool);
|
||||
|
||||
for (auto set : sets)
|
||||
state.set_nodes.make_vacant(set);
|
||||
|
||||
return { state.set_nodes.request_vacant(hash)->set, false };
|
||||
VkDescriptorSet vk_set = state.pools[pool_index]->sets[pool_offset];
|
||||
state.offset++;
|
||||
return vk_set;
|
||||
}
|
||||
|
||||
void DescriptorSetAllocator::clear()
|
||||
{
|
||||
for (auto &thr : per_thread)
|
||||
for (auto &state : per_thread_and_frame)
|
||||
{
|
||||
thr->set_nodes.clear();
|
||||
for (auto &pool : thr->pools)
|
||||
for (auto *obj : state.pools)
|
||||
{
|
||||
table.vkResetDescriptorPool(device->get_device(), pool, 0);
|
||||
table.vkDestroyDescriptorPool(device->get_device(), pool, nullptr);
|
||||
table.vkDestroyDescriptorPool(device->get_device(), obj->pool, nullptr);
|
||||
state.object_pool.free(obj);
|
||||
}
|
||||
thr->pools.clear();
|
||||
state.pools.clear();
|
||||
state.offset = 0;
|
||||
state.object_pool = {};
|
||||
}
|
||||
}
|
||||
|
||||
DescriptorSetAllocator::~DescriptorSetAllocator()
|
||||
{
|
||||
if (set_layout != VK_NULL_HANDLE)
|
||||
table.vkDestroyDescriptorSetLayout(device->get_device(), set_layout, nullptr);
|
||||
table.vkDestroyDescriptorSetLayout(device->get_device(), set_layout_pool, nullptr);
|
||||
table.vkDestroyDescriptorSetLayout(device->get_device(), set_layout_push, nullptr);
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -56,8 +56,8 @@ struct DescriptorSetLayout
|
||||
|
||||
// Avoid -Wclass-memaccess warnings since we hash DescriptorSetLayout.
|
||||
|
||||
static const unsigned VULKAN_NUM_SETS_PER_POOL = 16;
|
||||
static const unsigned VULKAN_DESCRIPTOR_RING_SIZE = 8;
|
||||
static const unsigned VULKAN_NUM_SETS_PER_POOL = 64;
|
||||
static const unsigned VULKAN_DESCRIPTOR_RING_SIZE = 16;
|
||||
|
||||
class DescriptorSetAllocator;
|
||||
class BindlessDescriptorPool;
|
||||
@@ -121,11 +121,16 @@ public:
|
||||
DescriptorSetAllocator(const DescriptorSetAllocator &) = delete;
|
||||
|
||||
void begin_frame();
|
||||
std::pair<VkDescriptorSet, bool> find(unsigned thread_index, Util::Hash hash);
|
||||
VkDescriptorSet request_descriptor_set(unsigned thread_index, unsigned frame_context);
|
||||
|
||||
VkDescriptorSetLayout get_layout() const
|
||||
VkDescriptorSetLayout get_layout_for_pool() const
|
||||
{
|
||||
return set_layout;
|
||||
return set_layout_pool;
|
||||
}
|
||||
|
||||
VkDescriptorSetLayout get_layout_for_push() const
|
||||
{
|
||||
return set_layout_push;
|
||||
}
|
||||
|
||||
void clear();
|
||||
@@ -140,27 +145,25 @@ public:
|
||||
void reset_bindless_pool(VkDescriptorPool pool);
|
||||
|
||||
private:
|
||||
struct DescriptorSetNode : Util::TemporaryHashmapEnabled<DescriptorSetNode>, Util::IntrusiveListEnabled<DescriptorSetNode>
|
||||
{
|
||||
explicit DescriptorSetNode(VkDescriptorSet set_)
|
||||
: set(set_)
|
||||
{
|
||||
}
|
||||
|
||||
VkDescriptorSet set;
|
||||
};
|
||||
|
||||
Device *device;
|
||||
const VolkDeviceTable &table;
|
||||
VkDescriptorSetLayout set_layout = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout set_layout_pool = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout set_layout_push = VK_NULL_HANDLE;
|
||||
|
||||
struct PerThread
|
||||
struct Pool
|
||||
{
|
||||
Util::TemporaryHashmap<DescriptorSetNode, VULKAN_DESCRIPTOR_RING_SIZE, true> set_nodes;
|
||||
std::vector<VkDescriptorPool> pools;
|
||||
bool should_begin = true;
|
||||
VkDescriptorPool pool;
|
||||
VkDescriptorSet sets[VULKAN_NUM_SETS_PER_POOL];
|
||||
};
|
||||
std::vector<std::unique_ptr<PerThread>> per_thread;
|
||||
|
||||
struct PerThreadAndFrame
|
||||
{
|
||||
std::vector<Pool *> pools;
|
||||
Util::ObjectPool<Pool> object_pool;
|
||||
uint32_t offset = 0;
|
||||
};
|
||||
|
||||
std::vector<PerThreadAndFrame> per_thread_and_frame;
|
||||
std::vector<VkDescriptorPoolSize> pool_size;
|
||||
bool bindless = false;
|
||||
};
|
||||
|
||||
@@ -502,6 +502,8 @@ const PipelineLayout *Device::request_pipeline_layout(const CombinedResourceLayo
|
||||
h.data(layout.spec_constant_mask, sizeof(layout.spec_constant_mask));
|
||||
h.u32(layout.attribute_mask);
|
||||
h.u32(layout.render_target_mask);
|
||||
// Drivers with and without push descriptor support need to observe different hashes for Fossilize.
|
||||
h.s32(int(ext.supports_push_descriptor));
|
||||
for (unsigned set = 0; set < VULKAN_NUM_DESCRIPTOR_SETS; set++)
|
||||
{
|
||||
Util::for_each_bit(layout.sets[set].immutable_sampler_mask, [&](unsigned bit) {
|
||||
@@ -874,7 +876,11 @@ void Device::init_workarounds()
|
||||
workarounds.emulate_event_as_pipeline_barrier = true;
|
||||
}
|
||||
|
||||
if (ext.driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY && gpu_props.driverVersion < VK_VERSION_MAJOR(535))
|
||||
// For whatever ridiculous reason, pipeline cache control causes GPU hangs on Pascal cards in parallel-rdp.
|
||||
// Use mesh shaders as the sentinel to check for that.
|
||||
if (ext.driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY &&
|
||||
(gpu_props.driverVersion < VK_VERSION_MAJOR(535) ||
|
||||
!ext.mesh_shader_features.meshShader))
|
||||
{
|
||||
LOGW("Disabling pipeline cache control.\n");
|
||||
workarounds.broken_pipeline_cache_control = true;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
namespace Vulkan
|
||||
{
|
||||
constexpr unsigned VULKAN_NUM_DESCRIPTOR_SETS = 4;
|
||||
constexpr unsigned VULKAN_NUM_DYNAMIC_UBOS = 8; // Vulkan min-spec
|
||||
constexpr unsigned VULKAN_NUM_BINDINGS = 32;
|
||||
constexpr unsigned VULKAN_NUM_BINDINGS_BINDLESS_VARYING = 16 * 1024;
|
||||
constexpr unsigned VULKAN_NUM_ATTACHMENTS = 8;
|
||||
|
||||
@@ -210,7 +210,7 @@ private:
|
||||
uint32_t height = 0;
|
||||
};
|
||||
|
||||
static const unsigned VULKAN_FRAMEBUFFER_RING_SIZE = 8;
|
||||
static const unsigned VULKAN_FRAMEBUFFER_RING_SIZE = 16;
|
||||
class FramebufferAllocator
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -65,16 +65,24 @@ PipelineLayout::PipelineLayout(Hash hash, Device *device_, const CombinedResourc
|
||||
{
|
||||
set_allocators[i] = device->request_descriptor_set_allocator(layout.sets[i], layout.stages_for_bindings[i],
|
||||
immutable_samplers ? immutable_samplers->samplers[i] : nullptr);
|
||||
layouts[i] = set_allocators[i]->get_layout();
|
||||
layouts[i] = set_allocators[i]->get_layout_for_pool();
|
||||
if (layout.descriptor_set_mask & (1u << i))
|
||||
{
|
||||
num_sets = i + 1;
|
||||
|
||||
// Assume the last set index in layout is the highest frequency update one, make that push descriptor if possible.
|
||||
// Only one descriptor set can be push descriptor.
|
||||
bool has_push_layout = set_allocators[i]->get_layout_for_push() != VK_NULL_HANDLE;
|
||||
if (has_push_layout)
|
||||
push_set_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_sets > device->get_gpu_properties().limits.maxBoundDescriptorSets)
|
||||
{
|
||||
LOGE("Number of sets %u exceeds device limit of %u.\n",
|
||||
num_sets, device->get_gpu_properties().limits.maxBoundDescriptorSets);
|
||||
}
|
||||
if (push_set_index != UINT32_MAX)
|
||||
layouts[push_set_index] = set_allocators[push_set_index]->get_layout_for_push();
|
||||
|
||||
if (num_sets > VULKAN_NUM_DESCRIPTOR_SETS)
|
||||
LOGE("Number of sets %u exceeds limit of %u.\n", num_sets, VULKAN_NUM_DESCRIPTOR_SETS);
|
||||
|
||||
VkPipelineLayoutCreateInfo info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
|
||||
if (num_sets)
|
||||
@@ -120,127 +128,169 @@ void PipelineLayout::create_update_templates()
|
||||
for_each_bit(set_layout.uniform_buffer_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = array_size;
|
||||
entry.offset = offsetof(ResourceBinding, buffer) + sizeof(ResourceBinding) * binding;
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
// Work around a RenderDoc capture bug where descriptorCount > 1 is not handled correctly.
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = desc_set == push_set_index ?
|
||||
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = i;
|
||||
entry.descriptorCount = 1;
|
||||
if (desc_set == push_set_index)
|
||||
entry.offset = offsetof(ResourceBinding, buffer.push) + sizeof(ResourceBinding) * (binding + i);
|
||||
else
|
||||
entry.offset = offsetof(ResourceBinding, buffer.dynamic) + sizeof(ResourceBinding) * (binding + i);
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
}
|
||||
});
|
||||
|
||||
for_each_bit(set_layout.storage_buffer_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = array_size;
|
||||
entry.offset = offsetof(ResourceBinding, buffer) + sizeof(ResourceBinding) * binding;
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = i;
|
||||
entry.descriptorCount = 1;
|
||||
entry.offset = offsetof(ResourceBinding, buffer.dynamic) + sizeof(ResourceBinding) * (binding + i);
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
}
|
||||
});
|
||||
|
||||
for_each_bit(set_layout.sampled_texel_buffer_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = array_size;
|
||||
entry.offset = offsetof(ResourceBinding, buffer_view) + sizeof(ResourceBinding) * binding;
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = i;
|
||||
entry.descriptorCount = 1;
|
||||
entry.offset = offsetof(ResourceBinding, buffer_view) + sizeof(ResourceBinding) * (binding + i);
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
}
|
||||
});
|
||||
|
||||
for_each_bit(set_layout.storage_texel_buffer_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = array_size;
|
||||
entry.offset = offsetof(ResourceBinding, buffer_view) + sizeof(ResourceBinding) * binding;
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = i;
|
||||
entry.descriptorCount = 1;
|
||||
entry.offset = offsetof(ResourceBinding, buffer_view) + sizeof(ResourceBinding) * (binding + i);
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
}
|
||||
});
|
||||
|
||||
for_each_bit(set_layout.sampled_image_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = array_size;
|
||||
if (set_layout.fp_mask & (1u << binding))
|
||||
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * binding;
|
||||
else
|
||||
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * binding;
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = i;
|
||||
entry.descriptorCount = 1;
|
||||
if (set_layout.fp_mask & (1u << binding))
|
||||
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * (binding + i);
|
||||
else
|
||||
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * (binding + i);
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
}
|
||||
});
|
||||
|
||||
for_each_bit(set_layout.separate_image_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = array_size;
|
||||
if (set_layout.fp_mask & (1u << binding))
|
||||
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * binding;
|
||||
else
|
||||
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * binding;
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = i;
|
||||
entry.descriptorCount = 1;
|
||||
if (set_layout.fp_mask & (1u << binding))
|
||||
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * (binding + i);
|
||||
else
|
||||
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * (binding + i);
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
}
|
||||
});
|
||||
|
||||
for_each_bit(set_layout.sampler_mask & ~set_layout.immutable_sampler_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = array_size;
|
||||
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * binding;
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = i;
|
||||
entry.descriptorCount = 1;
|
||||
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * (binding + i);
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
}
|
||||
});
|
||||
|
||||
for_each_bit(set_layout.storage_image_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = array_size;
|
||||
if (set_layout.fp_mask & (1u << binding))
|
||||
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * binding;
|
||||
else
|
||||
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * binding;
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = i;
|
||||
entry.descriptorCount = 1;
|
||||
if (set_layout.fp_mask & (1u << binding))
|
||||
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * (binding + i);
|
||||
else
|
||||
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * (binding + i);
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
}
|
||||
});
|
||||
|
||||
for_each_bit(set_layout.input_attachment_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = array_size;
|
||||
if (set_layout.fp_mask & (1u << binding))
|
||||
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * binding;
|
||||
else
|
||||
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * binding;
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = i;
|
||||
entry.descriptorCount = 1;
|
||||
if (set_layout.fp_mask & (1u << binding))
|
||||
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * (binding + i);
|
||||
else
|
||||
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * (binding + i);
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
}
|
||||
});
|
||||
|
||||
VkDescriptorUpdateTemplateCreateInfo info = {VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO };
|
||||
VkDescriptorUpdateTemplateCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO };
|
||||
info.pipelineLayout = pipe_layout;
|
||||
info.descriptorSetLayout = set_allocators[desc_set]->get_layout();
|
||||
info.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET;
|
||||
|
||||
if (desc_set == push_set_index)
|
||||
{
|
||||
info.descriptorSetLayout = set_allocators[desc_set]->get_layout_for_push();
|
||||
info.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR;
|
||||
}
|
||||
else
|
||||
{
|
||||
info.descriptorSetLayout = set_allocators[desc_set]->get_layout_for_pool();
|
||||
info.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET;
|
||||
}
|
||||
|
||||
info.set = desc_set;
|
||||
info.descriptorUpdateEntryCount = update_count;
|
||||
info.pDescriptorUpdateEntries = update_entries;
|
||||
|
||||
@@ -83,18 +83,21 @@ struct CombinedResourceLayout
|
||||
Util::Hash push_constant_layout_hash = 0;
|
||||
};
|
||||
|
||||
struct ResourceBinding
|
||||
union ResourceBinding
|
||||
{
|
||||
union {
|
||||
VkDescriptorBufferInfo buffer;
|
||||
struct
|
||||
{
|
||||
VkDescriptorImageInfo fp;
|
||||
VkDescriptorImageInfo integer;
|
||||
} image;
|
||||
VkBufferView buffer_view;
|
||||
};
|
||||
VkDeviceSize dynamic_offset;
|
||||
struct
|
||||
{
|
||||
VkDescriptorBufferInfo dynamic;
|
||||
VkDescriptorBufferInfo push;
|
||||
} buffer;
|
||||
|
||||
struct
|
||||
{
|
||||
VkDescriptorImageInfo fp;
|
||||
VkDescriptorImageInfo integer;
|
||||
} image;
|
||||
|
||||
VkBufferView buffer_view;
|
||||
};
|
||||
|
||||
struct ResourceBindings
|
||||
@@ -138,12 +141,18 @@ public:
|
||||
return update_template[set];
|
||||
}
|
||||
|
||||
uint32_t get_push_set_index() const
|
||||
{
|
||||
return push_set_index;
|
||||
}
|
||||
|
||||
private:
|
||||
Device *device;
|
||||
VkPipelineLayout pipe_layout = VK_NULL_HANDLE;
|
||||
CombinedResourceLayout layout;
|
||||
DescriptorSetAllocator *set_allocators[VULKAN_NUM_DESCRIPTOR_SETS] = {};
|
||||
VkDescriptorUpdateTemplate update_template[VULKAN_NUM_DESCRIPTOR_SETS] = {};
|
||||
uint32_t push_set_index = UINT32_MAX;
|
||||
void create_update_templates();
|
||||
};
|
||||
|
||||
|
||||
@@ -35,11 +35,16 @@
|
||||
#include "logging.hpp"
|
||||
#include <utility>
|
||||
|
||||
#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
|
||||
// Workaround silly Xlib headers that define macros for these globally :(
|
||||
#ifdef None
|
||||
#undef None
|
||||
#endif
|
||||
#ifdef Bool
|
||||
#undef Bool
|
||||
#endif
|
||||
#ifdef Status
|
||||
#undef Status
|
||||
#endif
|
||||
|
||||
#ifdef VULKAN_DEBUG
|
||||
#define VK_ASSERT(x) \
|
||||
|
||||
@@ -222,6 +222,9 @@ bool WSI::init_surface_swapchain_dxgi(unsigned width, unsigned height)
|
||||
case BackbufferFormat::HDR10:
|
||||
format = { VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_HDR10_ST2084_EXT };
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr unsigned num_images = 3;
|
||||
@@ -411,11 +414,13 @@ void WSI::nonblock_delete_swapchains()
|
||||
{
|
||||
if (!swap.fence || swap.fence->wait_timeout(0))
|
||||
{
|
||||
platform->destroy_swapchain_resources(swap.swapchain);
|
||||
table->vkDestroySwapchainKHR(device->get_device(), swap.swapchain, nullptr);
|
||||
}
|
||||
else if (pending >= 2)
|
||||
{
|
||||
swap.fence->wait();
|
||||
platform->destroy_swapchain_resources(swap.swapchain);
|
||||
table->vkDestroySwapchainKHR(device->get_device(), swap.swapchain, nullptr);
|
||||
}
|
||||
else
|
||||
@@ -448,6 +453,7 @@ void WSI::drain_swapchain(bool in_tear_down)
|
||||
{
|
||||
if (old_swap.fence)
|
||||
old_swap.fence->wait();
|
||||
platform->destroy_swapchain_resources(old_swap.swapchain);
|
||||
table->vkDestroySwapchainKHR(context->get_device(), old_swap.swapchain, nullptr);
|
||||
}
|
||||
|
||||
@@ -475,6 +481,7 @@ void WSI::tear_down_swapchain()
|
||||
|
||||
drain_swapchain(true);
|
||||
platform->event_swapchain_destroyed();
|
||||
platform->destroy_swapchain_resources(swapchain);
|
||||
table->vkDestroySwapchainKHR(context->get_device(), swapchain, nullptr);
|
||||
swapchain = VK_NULL_HANDLE;
|
||||
has_acquired_swapchain_index = false;
|
||||
@@ -1664,6 +1671,7 @@ WSI::SwapchainError WSI::init_swapchain(unsigned width, unsigned height)
|
||||
|
||||
platform->event_swapchain_destroyed();
|
||||
auto res = table->vkCreateSwapchainKHR(context->get_device(), &info, nullptr, &swapchain);
|
||||
platform->destroy_swapchain_resources(old_swapchain);
|
||||
table->vkDestroySwapchainKHR(context->get_device(), old_swapchain, nullptr);
|
||||
has_acquired_swapchain_index = false;
|
||||
present_id = 0;
|
||||
@@ -1749,6 +1757,7 @@ void WSIPlatform::event_swapchain_created(Device *, VkSwapchainKHR, unsigned, un
|
||||
VkFormat, VkColorSpaceKHR,
|
||||
VkSurfaceTransformFlagBitsKHR) {}
|
||||
void WSIPlatform::event_swapchain_destroyed() {}
|
||||
void WSIPlatform::destroy_swapchain_resources(VkSwapchainKHR) {}
|
||||
void WSIPlatform::event_frame_tick(double, double) {}
|
||||
void WSIPlatform::event_swapchain_index(Device *, unsigned) {}
|
||||
void WSIPlatform::begin_drop_event() {}
|
||||
|
||||
@@ -118,6 +118,7 @@ public:
|
||||
float aspect_ratio, size_t num_swapchain_images,
|
||||
VkFormat format, VkColorSpaceKHR color_space,
|
||||
VkSurfaceTransformFlagBitsKHR pre_rotate);
|
||||
virtual void destroy_swapchain_resources(VkSwapchainKHR swapchain);
|
||||
virtual void event_swapchain_destroyed();
|
||||
virtual void event_frame_tick(double frame, double elapsed);
|
||||
virtual void event_swapchain_index(Device *device, unsigned index);
|
||||
|
||||
Reference in New Issue
Block a user