Fuck git
This commit is contained in:
87
external/parallel-rdp/parallel-rdp-standalone/vulkan/buffer.cpp
vendored
Normal file
87
external/parallel-rdp/parallel-rdp-standalone/vulkan/buffer.cpp
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "buffer.hpp"
|
||||
#include "device.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
Buffer::Buffer(Device *device_, VkBuffer buffer_, const DeviceAllocation &alloc_, const BufferCreateInfo &info_,
|
||||
VkDeviceAddress bda_)
|
||||
: Cookie(device_)
|
||||
, device(device_)
|
||||
, buffer(buffer_)
|
||||
, alloc(alloc_)
|
||||
, info(info_)
|
||||
, bda(bda_)
|
||||
{
|
||||
}
|
||||
|
||||
ExternalHandle Buffer::export_handle()
|
||||
{
|
||||
return alloc.export_handle(*device);
|
||||
}
|
||||
|
||||
Buffer::~Buffer()
|
||||
{
|
||||
if (internal_sync)
|
||||
{
|
||||
device->destroy_buffer_nolock(buffer);
|
||||
device->free_memory_nolock(alloc);
|
||||
}
|
||||
else
|
||||
{
|
||||
device->destroy_buffer(buffer);
|
||||
device->free_memory(alloc);
|
||||
}
|
||||
}
|
||||
|
||||
void BufferDeleter::operator()(Buffer *buffer)
|
||||
{
|
||||
buffer->device->handle_pool.buffers.free(buffer);
|
||||
}
|
||||
|
||||
BufferView::BufferView(Device *device_, VkBufferView view_, const BufferViewCreateInfo &create_info_)
|
||||
: Cookie(device_)
|
||||
, device(device_)
|
||||
, view(view_)
|
||||
, info(create_info_)
|
||||
{
|
||||
}
|
||||
|
||||
BufferView::~BufferView()
|
||||
{
|
||||
if (view != VK_NULL_HANDLE)
|
||||
{
|
||||
if (internal_sync)
|
||||
device->destroy_buffer_view_nolock(view);
|
||||
else
|
||||
device->destroy_buffer_view(view);
|
||||
}
|
||||
}
|
||||
|
||||
void BufferViewDeleter::operator()(BufferView *view)
|
||||
{
|
||||
view->device->handle_pool.buffer_views.free(view);
|
||||
}
|
||||
|
||||
}
|
||||
162
external/parallel-rdp/parallel-rdp-standalone/vulkan/buffer.hpp
vendored
Normal file
162
external/parallel-rdp/parallel-rdp-standalone/vulkan/buffer.hpp
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cookie.hpp"
|
||||
#include "vulkan_common.hpp"
|
||||
#include "memory_allocator.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class Device;
|
||||
|
||||
enum class BufferDomain
|
||||
{
|
||||
Device, // Device local. Probably not visible from CPU.
|
||||
LinkedDeviceHost, // On desktop, directly mapped VRAM over PCI.
|
||||
LinkedDeviceHostPreferDevice, // Prefer device local of host visible.
|
||||
Host, // Host-only, needs to be synced to GPU. Might be device local as well on iGPUs.
|
||||
CachedHost,
|
||||
CachedCoherentHostPreferCoherent, // Aim for both cached and coherent, but prefer COHERENT
|
||||
CachedCoherentHostPreferCached, // Aim for both cached and coherent, but prefer CACHED
|
||||
};
|
||||
|
||||
enum BufferMiscFlagBits
|
||||
{
|
||||
BUFFER_MISC_ZERO_INITIALIZE_BIT = 1 << 0,
|
||||
BUFFER_MISC_EXTERNAL_MEMORY_BIT = 1 << 1
|
||||
};
|
||||
|
||||
using BufferMiscFlags = uint32_t;
|
||||
|
||||
struct BufferCreateInfo
|
||||
{
|
||||
BufferDomain domain = BufferDomain::Device;
|
||||
VkDeviceSize size = 0;
|
||||
VkBufferUsageFlags usage = 0;
|
||||
BufferMiscFlags misc = 0;
|
||||
VkMemoryRequirements allocation_requirements = {};
|
||||
ExternalHandle external;
|
||||
void *pnext = nullptr;
|
||||
};
|
||||
|
||||
class Buffer;
|
||||
struct BufferDeleter
|
||||
{
|
||||
void operator()(Buffer *buffer);
|
||||
};
|
||||
|
||||
class BufferView;
|
||||
struct BufferViewDeleter
|
||||
{
|
||||
void operator()(BufferView *view);
|
||||
};
|
||||
|
||||
class Buffer : public Util::IntrusivePtrEnabled<Buffer, BufferDeleter, HandleCounter>,
|
||||
public Cookie, public InternalSyncEnabled
|
||||
{
|
||||
public:
|
||||
friend struct BufferDeleter;
|
||||
~Buffer();
|
||||
|
||||
VkBuffer get_buffer() const
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const BufferCreateInfo &get_create_info() const
|
||||
{
|
||||
return info;
|
||||
}
|
||||
|
||||
DeviceAllocation &get_allocation()
|
||||
{
|
||||
return alloc;
|
||||
}
|
||||
|
||||
const DeviceAllocation &get_allocation() const
|
||||
{
|
||||
return alloc;
|
||||
}
|
||||
|
||||
ExternalHandle export_handle();
|
||||
|
||||
VkDeviceAddress get_device_address() const
|
||||
{
|
||||
VK_ASSERT(bda);
|
||||
return bda;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Util::ObjectPool<Buffer>;
|
||||
Buffer(Device *device, VkBuffer buffer, const DeviceAllocation &alloc, const BufferCreateInfo &info,
|
||||
VkDeviceAddress bda);
|
||||
|
||||
Device *device;
|
||||
VkBuffer buffer;
|
||||
DeviceAllocation alloc;
|
||||
BufferCreateInfo info;
|
||||
VkDeviceAddress bda;
|
||||
};
|
||||
using BufferHandle = Util::IntrusivePtr<Buffer>;
|
||||
|
||||
struct BufferViewCreateInfo
|
||||
{
|
||||
const Buffer *buffer;
|
||||
VkFormat format;
|
||||
VkDeviceSize offset;
|
||||
VkDeviceSize range;
|
||||
};
|
||||
|
||||
class BufferView : public Util::IntrusivePtrEnabled<BufferView, BufferViewDeleter, HandleCounter>,
|
||||
public Cookie, public InternalSyncEnabled
|
||||
{
|
||||
public:
|
||||
friend struct BufferViewDeleter;
|
||||
~BufferView();
|
||||
|
||||
VkBufferView get_view() const
|
||||
{
|
||||
return view;
|
||||
}
|
||||
|
||||
const BufferViewCreateInfo &get_create_info()
|
||||
{
|
||||
return info;
|
||||
}
|
||||
|
||||
const Buffer &get_buffer() const
|
||||
{
|
||||
return *info.buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Util::ObjectPool<BufferView>;
|
||||
BufferView(Device *device, VkBufferView view, const BufferViewCreateInfo &info);
|
||||
|
||||
Device *device;
|
||||
VkBufferView view;
|
||||
BufferViewCreateInfo info;
|
||||
};
|
||||
using BufferViewHandle = Util::IntrusivePtr<BufferView>;
|
||||
}
|
||||
138
external/parallel-rdp/parallel-rdp-standalone/vulkan/buffer_pool.cpp
vendored
Normal file
138
external/parallel-rdp/parallel-rdp-standalone/vulkan/buffer_pool.cpp
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include "buffer_pool.hpp"
|
||||
#include "device.hpp"
|
||||
#include <utility>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
void BufferPool::init(Device *device_, VkDeviceSize block_size_,
|
||||
VkDeviceSize alignment_, VkBufferUsageFlags usage_)
|
||||
{
|
||||
device = device_;
|
||||
block_size = block_size_;
|
||||
alignment = alignment_;
|
||||
usage = usage_;
|
||||
}
|
||||
|
||||
void BufferPool::set_spill_region_size(VkDeviceSize spill_size_)
|
||||
{
|
||||
spill_size = spill_size_;
|
||||
}
|
||||
|
||||
void BufferPool::set_max_retained_blocks(size_t max_blocks)
|
||||
{
|
||||
max_retained_blocks = max_blocks;
|
||||
}
|
||||
|
||||
BufferBlock::~BufferBlock()
|
||||
{
|
||||
}
|
||||
|
||||
void BufferPool::reset()
|
||||
{
|
||||
blocks.clear();
|
||||
}
|
||||
|
||||
BufferBlock BufferPool::allocate_block(VkDeviceSize size)
|
||||
{
|
||||
BufferDomain ideal_domain = ((usage & VK_BUFFER_USAGE_TRANSFER_SRC_BIT) != 0) ?
|
||||
BufferDomain::Host : BufferDomain::LinkedDeviceHost;
|
||||
|
||||
BufferBlock block;
|
||||
|
||||
BufferCreateInfo info;
|
||||
info.domain = ideal_domain;
|
||||
info.size = size;
|
||||
info.usage = usage;
|
||||
|
||||
block.buffer = device->create_buffer(info, nullptr);
|
||||
device->set_name(*block.buffer, "chain-allocated-block");
|
||||
block.buffer->set_internal_sync_object();
|
||||
|
||||
// Try to map it, will fail unless the memory is host visible.
|
||||
block.mapped = static_cast<uint8_t *>(device->map_host_buffer(*block.buffer, MEMORY_ACCESS_WRITE_BIT));
|
||||
|
||||
block.offset = 0;
|
||||
block.alignment = alignment;
|
||||
block.size = size;
|
||||
block.spill_size = spill_size;
|
||||
return block;
|
||||
}
|
||||
|
||||
BufferBlock BufferPool::request_block(VkDeviceSize minimum_size)
|
||||
{
|
||||
if ((minimum_size > block_size) || blocks.empty())
|
||||
{
|
||||
return allocate_block(std::max(block_size, minimum_size));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto back = std::move(blocks.back());
|
||||
blocks.pop_back();
|
||||
|
||||
back.mapped = static_cast<uint8_t *>(device->map_host_buffer(*back.buffer, MEMORY_ACCESS_WRITE_BIT));
|
||||
back.offset = 0;
|
||||
return back;
|
||||
}
|
||||
}
|
||||
|
||||
void BufferPool::recycle_block(BufferBlock &block)
|
||||
{
|
||||
VK_ASSERT(block.size == block_size);
|
||||
|
||||
if (blocks.size() < max_retained_blocks)
|
||||
blocks.push_back(std::move(block));
|
||||
else
|
||||
block = {};
|
||||
}
|
||||
|
||||
BufferPool::~BufferPool()
|
||||
{
|
||||
VK_ASSERT(blocks.empty());
|
||||
}
|
||||
|
||||
BufferBlockAllocation BufferBlock::allocate(VkDeviceSize allocate_size)
|
||||
{
|
||||
auto aligned_offset = (offset + alignment - 1) & ~(alignment - 1);
|
||||
if (aligned_offset + allocate_size <= size)
|
||||
{
|
||||
auto *ret = mapped + aligned_offset;
|
||||
offset = aligned_offset + allocate_size;
|
||||
|
||||
VkDeviceSize padded_size = std::max<VkDeviceSize>(allocate_size, spill_size);
|
||||
padded_size = std::min<VkDeviceSize>(padded_size, size - aligned_offset);
|
||||
|
||||
return { ret, buffer, aligned_offset, padded_size };
|
||||
}
|
||||
else
|
||||
return { nullptr, {}, 0, 0 };
|
||||
}
|
||||
|
||||
void BufferBlock::unmap(Device &device)
|
||||
{
|
||||
device.unmap_host_buffer(*buffer, MEMORY_ACCESS_WRITE_BIT);
|
||||
mapped = nullptr;
|
||||
}
|
||||
}
|
||||
96
external/parallel-rdp/parallel-rdp-standalone/vulkan/buffer_pool.hpp
vendored
Normal file
96
external/parallel-rdp/parallel-rdp-standalone/vulkan/buffer_pool.hpp
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vulkan_headers.hpp"
|
||||
#include "intrusive.hpp"
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class Device;
|
||||
class Buffer;
|
||||
|
||||
struct BufferBlockAllocation
|
||||
{
|
||||
uint8_t *host;
|
||||
Util::IntrusivePtr<Buffer> buffer;
|
||||
VkDeviceSize offset;
|
||||
VkDeviceSize padded_size;
|
||||
};
|
||||
|
||||
class BufferBlock
|
||||
{
|
||||
public:
|
||||
~BufferBlock();
|
||||
|
||||
BufferBlockAllocation allocate(VkDeviceSize allocate_size);
|
||||
inline bool is_mapped() const { return mapped != nullptr; }
|
||||
const Buffer &get_buffer() const { return *buffer; }
|
||||
void unmap(Device &device);
|
||||
|
||||
inline VkDeviceSize get_offset() const { return offset; }
|
||||
inline VkDeviceSize get_size() const { return size; }
|
||||
|
||||
private:
|
||||
friend class BufferPool;
|
||||
Util::IntrusivePtr<Buffer> buffer;
|
||||
VkDeviceSize offset = 0;
|
||||
VkDeviceSize alignment = 0;
|
||||
VkDeviceSize size = 0;
|
||||
VkDeviceSize spill_size = 0;
|
||||
uint8_t *mapped = nullptr;
|
||||
};
|
||||
|
||||
class BufferPool
|
||||
{
|
||||
public:
|
||||
~BufferPool();
|
||||
void init(Device *device, VkDeviceSize block_size, VkDeviceSize alignment, VkBufferUsageFlags usage);
|
||||
void reset();
|
||||
|
||||
// Used for allocating UBOs, where we want to specify a fixed size for range,
|
||||
// and we need to make sure we don't allocate beyond the block.
|
||||
void set_spill_region_size(VkDeviceSize spill_size);
|
||||
void set_max_retained_blocks(size_t max_blocks);
|
||||
|
||||
VkDeviceSize get_block_size() const
|
||||
{
|
||||
return block_size;
|
||||
}
|
||||
|
||||
BufferBlock request_block(VkDeviceSize minimum_size);
|
||||
void recycle_block(BufferBlock &block);
|
||||
|
||||
private:
|
||||
Device *device = nullptr;
|
||||
VkDeviceSize block_size = 0;
|
||||
VkDeviceSize alignment = 0;
|
||||
VkDeviceSize spill_size = 0;
|
||||
VkBufferUsageFlags usage = 0;
|
||||
size_t max_retained_blocks = 0;
|
||||
std::vector<BufferBlock> blocks;
|
||||
BufferBlock allocate_block(VkDeviceSize size);
|
||||
};
|
||||
}
|
||||
3360
external/parallel-rdp/parallel-rdp-standalone/vulkan/command_buffer.cpp
vendored
Normal file
3360
external/parallel-rdp/parallel-rdp-standalone/vulkan/command_buffer.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
958
external/parallel-rdp/parallel-rdp-standalone/vulkan/command_buffer.hpp
vendored
Normal file
958
external/parallel-rdp/parallel-rdp-standalone/vulkan/command_buffer.hpp
vendored
Normal file
@@ -0,0 +1,958 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "buffer.hpp"
|
||||
#include "buffer_pool.hpp"
|
||||
#include "vulkan_headers.hpp"
|
||||
#include "image.hpp"
|
||||
#include "pipeline_event.hpp"
|
||||
#include "query_pool.hpp"
|
||||
#include "render_pass.hpp"
|
||||
#include "sampler.hpp"
|
||||
#include "shader.hpp"
|
||||
#include "vulkan_common.hpp"
|
||||
#include <string.h>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class DebugChannelInterface;
|
||||
class IndirectLayout;
|
||||
|
||||
static inline VkPipelineStageFlags convert_vk_stage2(VkPipelineStageFlags2 stages)
|
||||
{
|
||||
constexpr VkPipelineStageFlags2 transfer_mask =
|
||||
VK_PIPELINE_STAGE_2_COPY_BIT |
|
||||
VK_PIPELINE_STAGE_2_BLIT_BIT |
|
||||
VK_PIPELINE_STAGE_2_RESOLVE_BIT |
|
||||
VK_PIPELINE_STAGE_2_CLEAR_BIT |
|
||||
VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_COPY_BIT_KHR;
|
||||
|
||||
constexpr VkPipelineStageFlags2 preraster_mask =
|
||||
VK_PIPELINE_STAGE_2_PRE_RASTERIZATION_SHADERS_BIT;
|
||||
|
||||
if ((stages & transfer_mask) != 0)
|
||||
{
|
||||
stages |= VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
stages &= ~transfer_mask;
|
||||
}
|
||||
|
||||
if ((stages & preraster_mask) != 0)
|
||||
{
|
||||
// TODO: Augment if we add mesh shader support eventually.
|
||||
stages |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
|
||||
stages &= ~preraster_mask;
|
||||
}
|
||||
|
||||
return VkPipelineStageFlags(stages);
|
||||
}
|
||||
|
||||
static inline VkPipelineStageFlags convert_vk_src_stage2(VkPipelineStageFlags2 stages)
|
||||
{
|
||||
stages = convert_vk_stage2(stages);
|
||||
if (stages == VK_PIPELINE_STAGE_NONE)
|
||||
stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
return VkPipelineStageFlags(stages);
|
||||
}
|
||||
|
||||
static inline VkPipelineStageFlags convert_vk_dst_stage2(VkPipelineStageFlags2 stages)
|
||||
{
|
||||
stages = convert_vk_stage2(stages);
|
||||
if (stages == VK_PIPELINE_STAGE_NONE)
|
||||
stages = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
return VkPipelineStageFlags(stages);
|
||||
}
|
||||
|
||||
static inline VkAccessFlags convert_vk_access_flags2(VkAccessFlags2 access)
|
||||
{
|
||||
constexpr VkAccessFlags2 sampled_mask =
|
||||
VK_ACCESS_2_SHADER_SAMPLED_READ_BIT |
|
||||
VK_ACCESS_2_SHADER_STORAGE_READ_BIT |
|
||||
VK_ACCESS_2_SHADER_BINDING_TABLE_READ_BIT_KHR;
|
||||
|
||||
constexpr VkAccessFlags2 storage_mask =
|
||||
VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT;
|
||||
|
||||
if ((access & sampled_mask) != 0)
|
||||
{
|
||||
access |= VK_ACCESS_SHADER_READ_BIT;
|
||||
access &= ~sampled_mask;
|
||||
}
|
||||
|
||||
if ((access & storage_mask) != 0)
|
||||
{
|
||||
access |= VK_ACCESS_SHADER_WRITE_BIT;
|
||||
access &= ~storage_mask;
|
||||
}
|
||||
|
||||
return VkAccessFlags(access);
|
||||
}
|
||||
|
||||
enum CommandBufferDirtyBits
|
||||
{
|
||||
COMMAND_BUFFER_DIRTY_STATIC_STATE_BIT = 1 << 0,
|
||||
COMMAND_BUFFER_DIRTY_PIPELINE_BIT = 1 << 1,
|
||||
|
||||
COMMAND_BUFFER_DIRTY_VIEWPORT_BIT = 1 << 2,
|
||||
COMMAND_BUFFER_DIRTY_SCISSOR_BIT = 1 << 3,
|
||||
COMMAND_BUFFER_DIRTY_DEPTH_BIAS_BIT = 1 << 4,
|
||||
COMMAND_BUFFER_DIRTY_STENCIL_REFERENCE_BIT = 1 << 5,
|
||||
|
||||
COMMAND_BUFFER_DIRTY_STATIC_VERTEX_BIT = 1 << 6,
|
||||
|
||||
COMMAND_BUFFER_DIRTY_PUSH_CONSTANTS_BIT = 1 << 7,
|
||||
|
||||
COMMAND_BUFFER_DYNAMIC_BITS = COMMAND_BUFFER_DIRTY_VIEWPORT_BIT | COMMAND_BUFFER_DIRTY_SCISSOR_BIT |
|
||||
COMMAND_BUFFER_DIRTY_DEPTH_BIAS_BIT |
|
||||
COMMAND_BUFFER_DIRTY_STENCIL_REFERENCE_BIT
|
||||
};
|
||||
using CommandBufferDirtyFlags = uint32_t;
|
||||
|
||||
#define COMPARE_OP_BITS 3
|
||||
#define STENCIL_OP_BITS 3
|
||||
#define BLEND_FACTOR_BITS 5
|
||||
#define BLEND_OP_BITS 3
|
||||
#define CULL_MODE_BITS 2
|
||||
#define FRONT_FACE_BITS 1
|
||||
#define TOPOLOGY_BITS 4
|
||||
union PipelineState {
|
||||
struct
|
||||
{
|
||||
// Word 0, tightly packed.
|
||||
uint32_t depth_write : 1;
|
||||
uint32_t depth_test : 1;
|
||||
uint32_t blend_enable : 1;
|
||||
uint32_t cull_mode : CULL_MODE_BITS;
|
||||
uint32_t front_face : FRONT_FACE_BITS;
|
||||
uint32_t depth_compare : COMPARE_OP_BITS;
|
||||
uint32_t depth_bias_enable : 1;
|
||||
uint32_t stencil_test : 1;
|
||||
uint32_t stencil_front_fail : STENCIL_OP_BITS;
|
||||
uint32_t stencil_front_pass : STENCIL_OP_BITS;
|
||||
uint32_t stencil_front_depth_fail : STENCIL_OP_BITS;
|
||||
uint32_t stencil_front_compare_op : COMPARE_OP_BITS;
|
||||
uint32_t stencil_back_fail : STENCIL_OP_BITS;
|
||||
uint32_t stencil_back_pass : STENCIL_OP_BITS;
|
||||
uint32_t stencil_back_depth_fail : STENCIL_OP_BITS;
|
||||
|
||||
// Word 1, tightly packed.
|
||||
uint32_t stencil_back_compare_op : COMPARE_OP_BITS;
|
||||
uint32_t alpha_to_coverage : 1;
|
||||
uint32_t alpha_to_one : 1;
|
||||
uint32_t sample_shading : 1;
|
||||
uint32_t src_color_blend : BLEND_FACTOR_BITS;
|
||||
uint32_t dst_color_blend : BLEND_FACTOR_BITS;
|
||||
uint32_t color_blend_op : BLEND_OP_BITS;
|
||||
uint32_t src_alpha_blend : BLEND_FACTOR_BITS;
|
||||
uint32_t dst_alpha_blend : BLEND_FACTOR_BITS;
|
||||
uint32_t alpha_blend_op : BLEND_OP_BITS;
|
||||
|
||||
// Word 2, tightly packed.
|
||||
uint32_t primitive_restart : 1;
|
||||
uint32_t topology : TOPOLOGY_BITS;
|
||||
uint32_t wireframe : 1;
|
||||
uint32_t subgroup_control_size : 1;
|
||||
uint32_t subgroup_full_group : 1;
|
||||
uint32_t subgroup_minimum_size_log2 : 3;
|
||||
uint32_t subgroup_maximum_size_log2 : 3;
|
||||
uint32_t subgroup_control_size_task : 1;
|
||||
uint32_t subgroup_full_group_task : 1;
|
||||
uint32_t subgroup_minimum_size_log2_task : 3;
|
||||
uint32_t subgroup_maximum_size_log2_task : 3;
|
||||
uint32_t conservative_raster : 1;
|
||||
uint32_t padding : 9;
|
||||
|
||||
// Word 3
|
||||
uint32_t write_mask;
|
||||
} state;
|
||||
uint32_t words[4];
|
||||
};
|
||||
|
||||
struct PotentialState
|
||||
{
|
||||
float blend_constants[4];
|
||||
uint32_t spec_constants[VULKAN_NUM_TOTAL_SPEC_CONSTANTS];
|
||||
uint8_t spec_constant_mask;
|
||||
uint8_t internal_spec_constant_mask;
|
||||
};
|
||||
|
||||
struct DynamicState
|
||||
{
|
||||
float depth_bias_constant = 0.0f;
|
||||
float depth_bias_slope = 0.0f;
|
||||
uint8_t front_compare_mask = 0;
|
||||
uint8_t front_write_mask = 0;
|
||||
uint8_t front_reference = 0;
|
||||
uint8_t back_compare_mask = 0;
|
||||
uint8_t back_write_mask = 0;
|
||||
uint8_t back_reference = 0;
|
||||
};
|
||||
|
||||
struct VertexAttribState
|
||||
{
|
||||
uint32_t binding;
|
||||
VkFormat format;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
struct IndexState
|
||||
{
|
||||
VkBuffer buffer;
|
||||
VkDeviceSize offset;
|
||||
VkIndexType index_type;
|
||||
};
|
||||
|
||||
struct VertexBindingState
|
||||
{
|
||||
VkBuffer buffers[VULKAN_NUM_VERTEX_BUFFERS];
|
||||
VkDeviceSize offsets[VULKAN_NUM_VERTEX_BUFFERS];
|
||||
};
|
||||
|
||||
enum CommandBufferSavedStateBits
|
||||
{
|
||||
COMMAND_BUFFER_SAVED_BINDINGS_0_BIT = 1u << 0,
|
||||
COMMAND_BUFFER_SAVED_BINDINGS_1_BIT = 1u << 1,
|
||||
COMMAND_BUFFER_SAVED_BINDINGS_2_BIT = 1u << 2,
|
||||
COMMAND_BUFFER_SAVED_BINDINGS_3_BIT = 1u << 3,
|
||||
COMMAND_BUFFER_SAVED_VIEWPORT_BIT = 1u << 4,
|
||||
COMMAND_BUFFER_SAVED_SCISSOR_BIT = 1u << 5,
|
||||
COMMAND_BUFFER_SAVED_RENDER_STATE_BIT = 1u << 6,
|
||||
COMMAND_BUFFER_SAVED_PUSH_CONSTANT_BIT = 1u << 7
|
||||
};
|
||||
static_assert(VULKAN_NUM_DESCRIPTOR_SETS == 4, "Number of descriptor sets != 4.");
|
||||
using CommandBufferSaveStateFlags = uint32_t;
|
||||
|
||||
struct CommandBufferSavedState
|
||||
{
|
||||
CommandBufferSaveStateFlags flags;
|
||||
ResourceBindings bindings;
|
||||
VkViewport viewport;
|
||||
VkRect2D scissor;
|
||||
|
||||
PipelineState static_state;
|
||||
PotentialState potential_static_state;
|
||||
DynamicState dynamic_state;
|
||||
};
|
||||
|
||||
struct DeferredPipelineCompile
|
||||
{
|
||||
Program *program;
|
||||
const PipelineLayout *layout;
|
||||
std::vector<Program *> program_group;
|
||||
|
||||
const RenderPass *compatible_render_pass;
|
||||
PipelineState static_state;
|
||||
PotentialState potential_static_state;
|
||||
VertexAttribState attribs[VULKAN_NUM_VERTEX_ATTRIBS];
|
||||
VkDeviceSize strides[VULKAN_NUM_VERTEX_BUFFERS];
|
||||
VkVertexInputRate input_rates[VULKAN_NUM_VERTEX_BUFFERS];
|
||||
|
||||
unsigned subpass_index;
|
||||
Util::Hash hash;
|
||||
VkPipelineCache cache;
|
||||
uint32_t subgroup_size_tag;
|
||||
};
|
||||
|
||||
class CommandBuffer;
|
||||
struct CommandBufferDeleter
|
||||
{
|
||||
void operator()(CommandBuffer *cmd);
|
||||
};
|
||||
|
||||
class Device;
|
||||
class CommandBuffer : public Util::IntrusivePtrEnabled<CommandBuffer, CommandBufferDeleter, HandleCounter>
|
||||
{
|
||||
public:
|
||||
friend struct CommandBufferDeleter;
|
||||
enum class Type
|
||||
{
|
||||
Generic = QUEUE_INDEX_GRAPHICS,
|
||||
AsyncCompute = QUEUE_INDEX_COMPUTE,
|
||||
AsyncTransfer = QUEUE_INDEX_TRANSFER,
|
||||
VideoDecode = QUEUE_INDEX_VIDEO_DECODE,
|
||||
VideoEncode = QUEUE_INDEX_VIDEO_ENCODE,
|
||||
Count
|
||||
};
|
||||
|
||||
~CommandBuffer();
|
||||
VkCommandBuffer get_command_buffer() const
|
||||
{
|
||||
return cmd;
|
||||
}
|
||||
|
||||
void begin_region(const char *name, const float *color = nullptr);
|
||||
void insert_label(const char *name, const float *color = nullptr);
|
||||
void end_region();
|
||||
|
||||
Device &get_device()
|
||||
{
|
||||
return *device;
|
||||
}
|
||||
|
||||
VkPipelineStageFlags2 swapchain_touched_in_stages() const
|
||||
{
|
||||
return uses_swapchain_in_stages;
|
||||
}
|
||||
|
||||
// Only used when using swapchain in non-obvious ways, like compute or transfer.
|
||||
void swapchain_touch_in_stages(VkPipelineStageFlags2 stages)
|
||||
{
|
||||
uses_swapchain_in_stages |= stages;
|
||||
}
|
||||
|
||||
void set_thread_index(unsigned index_)
|
||||
{
|
||||
thread_index = index_;
|
||||
}
|
||||
|
||||
unsigned get_thread_index() const
|
||||
{
|
||||
return thread_index;
|
||||
}
|
||||
|
||||
void set_is_secondary()
|
||||
{
|
||||
is_secondary = true;
|
||||
}
|
||||
|
||||
bool get_is_secondary() const
|
||||
{
|
||||
return is_secondary;
|
||||
}
|
||||
|
||||
void clear_image(const Image &image, const VkClearValue &value);
|
||||
void clear_image(const Image &image, const VkClearValue &value, VkImageAspectFlags aspect);
|
||||
void clear_quad(unsigned attachment, const VkClearRect &rect, const VkClearValue &value,
|
||||
VkImageAspectFlags = VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
void clear_quad(const VkClearRect &rect, const VkClearAttachment *attachments, unsigned num_attachments);
|
||||
|
||||
void fill_buffer(const Buffer &dst, uint32_t value);
|
||||
void fill_buffer(const Buffer &dst, uint32_t value, VkDeviceSize offset, VkDeviceSize size);
|
||||
void copy_buffer(const Buffer &dst, VkDeviceSize dst_offset, const Buffer &src, VkDeviceSize src_offset,
|
||||
VkDeviceSize size);
|
||||
void copy_buffer(const Buffer &dst, const Buffer &src);
|
||||
void copy_buffer(const Buffer &dst, const Buffer &src, const VkBufferCopy *copies, size_t count);
|
||||
void copy_image(const Image &dst, const Image &src);
|
||||
void copy_image(const Image &dst, const Image &src,
|
||||
const VkOffset3D &dst_offset, const VkOffset3D &src_offset,
|
||||
const VkExtent3D &extent,
|
||||
const VkImageSubresourceLayers &dst_subresource,
|
||||
const VkImageSubresourceLayers &src_subresource);
|
||||
|
||||
void copy_buffer_to_image(const Image &image, const Buffer &buffer, VkDeviceSize buffer_offset,
|
||||
const VkOffset3D &offset, const VkExtent3D &extent, unsigned row_length,
|
||||
unsigned slice_height, const VkImageSubresourceLayers &subresrouce);
|
||||
void copy_buffer_to_image(const Image &image, const Buffer &buffer, unsigned num_blits, const VkBufferImageCopy *blits);
|
||||
|
||||
void copy_image_to_buffer(const Buffer &buffer, const Image &image, unsigned num_blits, const VkBufferImageCopy *blits);
|
||||
void copy_image_to_buffer(const Buffer &dst, const Image &src, VkDeviceSize buffer_offset, const VkOffset3D &offset,
|
||||
const VkExtent3D &extent, unsigned row_length, unsigned slice_height,
|
||||
const VkImageSubresourceLayers &subresrouce);
|
||||
|
||||
void full_barrier();
|
||||
void pixel_barrier();
|
||||
|
||||
// Simplified global memory barrier.
|
||||
void barrier(VkPipelineStageFlags2 src_stage, VkAccessFlags2 src_access,
|
||||
VkPipelineStageFlags2 dst_stage, VkAccessFlags2 dst_access);
|
||||
|
||||
PipelineEvent signal_event(const VkDependencyInfo &dep);
|
||||
void wait_events(uint32_t num_events, const PipelineEvent *events, const VkDependencyInfo *deps);
|
||||
|
||||
// Full expressive barrier.
|
||||
void barrier(const VkDependencyInfo &dep);
|
||||
|
||||
void buffer_barrier(const Buffer &buffer,
|
||||
VkPipelineStageFlags2 src_stage, VkAccessFlags2 src_access,
|
||||
VkPipelineStageFlags2 dst_stage, VkAccessFlags2 dst_access);
|
||||
|
||||
void image_barrier(const Image &image,
|
||||
VkImageLayout old_layout, VkImageLayout new_layout,
|
||||
VkPipelineStageFlags2 src_stage, VkAccessFlags2 src_access,
|
||||
VkPipelineStageFlags2 dst_stage, VkAccessFlags2 dst_access);
|
||||
|
||||
void buffer_barriers(uint32_t buffer_barriers, const VkBufferMemoryBarrier2 *buffers);
|
||||
void image_barriers(uint32_t image_barriers, const VkImageMemoryBarrier2 *images);
|
||||
|
||||
void release_buffer_barrier(const Buffer &buffer, VkPipelineStageFlags2 src_stage, VkAccessFlags2 src_access,
|
||||
uint32_t dst_queue_family = VK_QUEUE_FAMILY_EXTERNAL);
|
||||
void acquire_buffer_barrier(const Buffer &buffer, VkPipelineStageFlags2 dst_stage, VkAccessFlags2 dst_access,
|
||||
uint32_t src_queue_family = VK_QUEUE_FAMILY_EXTERNAL);
|
||||
void release_image_barrier(const Image &image,
|
||||
VkImageLayout old_layout, VkImageLayout new_layout,
|
||||
VkPipelineStageFlags2 src_stage, VkAccessFlags2 src_access,
|
||||
uint32_t dst_queue_family = VK_QUEUE_FAMILY_EXTERNAL);
|
||||
void acquire_image_barrier(const Image &image, VkImageLayout old_layout, VkImageLayout new_layout,
|
||||
VkPipelineStageFlags2 dst_stage, VkAccessFlags2 dst_access,
|
||||
uint32_t src_queue_family = VK_QUEUE_FAMILY_EXTERNAL);
|
||||
|
||||
void blit_image(const Image &dst,
|
||||
const Image &src,
|
||||
const VkOffset3D &dst_offset0, const VkOffset3D &dst_extent,
|
||||
const VkOffset3D &src_offset0, const VkOffset3D &src_extent, unsigned dst_level, unsigned src_level,
|
||||
unsigned dst_base_layer = 0, uint32_t src_base_layer = 0, unsigned num_layers = 1,
|
||||
VkFilter filter = VK_FILTER_LINEAR);
|
||||
|
||||
// Prepares an image to have its mipmap generated.
|
||||
// Puts the top-level into TRANSFER_SRC_OPTIMAL, and all other levels are invalidated with an UNDEFINED -> TRANSFER_DST_OPTIMAL.
|
||||
void barrier_prepare_generate_mipmap(const Image &image, VkImageLayout base_level_layout,
|
||||
VkPipelineStageFlags2 src_stage, VkAccessFlags2 src_access,
|
||||
bool need_top_level_barrier = true);
|
||||
|
||||
// The image must have been transitioned with barrier_prepare_generate_mipmap before calling this function.
|
||||
// After calling this function, the image will be entirely in TRANSFER_SRC_OPTIMAL layout.
|
||||
// Wait for TRANSFER stage to drain before transitioning away from TRANSFER_SRC_OPTIMAL.
|
||||
void generate_mipmap(const Image &image);
|
||||
|
||||
void begin_render_pass(const RenderPassInfo &info, VkSubpassContents contents = VK_SUBPASS_CONTENTS_INLINE);
|
||||
void next_subpass(VkSubpassContents contents = VK_SUBPASS_CONTENTS_INLINE);
|
||||
void end_render_pass();
|
||||
void submit_secondary(Util::IntrusivePtr<CommandBuffer> secondary);
|
||||
inline unsigned get_current_subpass() const
|
||||
{
|
||||
return pipeline_state.subpass_index;
|
||||
}
|
||||
Util::IntrusivePtr<CommandBuffer> request_secondary_command_buffer(unsigned thread_index, unsigned subpass);
|
||||
static Util::IntrusivePtr<CommandBuffer> request_secondary_command_buffer(Device &device,
|
||||
const RenderPassInfo &rp, unsigned thread_index, unsigned subpass);
|
||||
|
||||
void set_program(Program *program);
|
||||
void set_program_group(Program * const *programs, unsigned num_programs, const PipelineLayout *layout);
|
||||
|
||||
#ifdef GRANITE_VULKAN_SYSTEM_HANDLES
|
||||
// Convenience functions for one-off shader binds.
|
||||
void set_program(const std::string &task, const std::string &mesh, const std::string &fragment,
|
||||
const std::vector<std::pair<std::string, int>> &defines = {});
|
||||
void set_program(const std::string &vertex, const std::string &fragment,
|
||||
const std::vector<std::pair<std::string, int>> &defines = {});
|
||||
void set_program(const std::string &compute,
|
||||
const std::vector<std::pair<std::string, int>> &defines = {});
|
||||
#endif
|
||||
|
||||
void set_buffer_view(unsigned set, unsigned binding, const BufferView &view);
|
||||
void set_storage_buffer_view(unsigned set, unsigned binding, const BufferView &view);
|
||||
void set_input_attachments(unsigned set, unsigned start_binding);
|
||||
void set_texture(unsigned set, unsigned binding, const ImageView &view);
|
||||
void set_unorm_texture(unsigned set, unsigned binding, const ImageView &view);
|
||||
void set_srgb_texture(unsigned set, unsigned binding, const ImageView &view);
|
||||
void set_texture(unsigned set, unsigned binding, const ImageView &view, const Sampler &sampler);
|
||||
void set_texture(unsigned set, unsigned binding, const ImageView &view, StockSampler sampler);
|
||||
void set_storage_texture(unsigned set, unsigned binding, const ImageView &view);
|
||||
void set_unorm_storage_texture(unsigned set, unsigned binding, const ImageView &view);
|
||||
void set_sampler(unsigned set, unsigned binding, const Sampler &sampler);
|
||||
void set_sampler(unsigned set, unsigned binding, StockSampler sampler);
|
||||
void set_uniform_buffer(unsigned set, unsigned binding, const Buffer &buffer);
|
||||
void set_uniform_buffer(unsigned set, unsigned binding, const Buffer &buffer, VkDeviceSize offset,
|
||||
VkDeviceSize range);
|
||||
void set_storage_buffer(unsigned set, unsigned binding, const Buffer &buffer);
|
||||
void set_storage_buffer(unsigned set, unsigned binding, const Buffer &buffer, VkDeviceSize offset,
|
||||
VkDeviceSize range);
|
||||
|
||||
void set_bindless(unsigned set, VkDescriptorSet desc_set);
|
||||
|
||||
void push_constants(const void *data, VkDeviceSize offset, VkDeviceSize range);
|
||||
|
||||
void *allocate_constant_data(unsigned set, unsigned binding, VkDeviceSize size);
|
||||
|
||||
template <typename T>
|
||||
T *allocate_typed_constant_data(unsigned set, unsigned binding, unsigned count)
|
||||
{
|
||||
return static_cast<T *>(allocate_constant_data(set, binding, count * sizeof(T)));
|
||||
}
|
||||
|
||||
void *allocate_vertex_data(unsigned binding, VkDeviceSize size, VkDeviceSize stride,
|
||||
VkVertexInputRate step_rate = VK_VERTEX_INPUT_RATE_VERTEX);
|
||||
void *allocate_index_data(VkDeviceSize size, VkIndexType index_type);
|
||||
|
||||
void *update_buffer(const Buffer &buffer, VkDeviceSize offset, VkDeviceSize size);
|
||||
void *update_image(const Image &image, const VkOffset3D &offset, const VkExtent3D &extent, uint32_t row_length,
|
||||
uint32_t image_height, const VkImageSubresourceLayers &subresource);
|
||||
void *update_image(const Image &image, uint32_t row_length = 0, uint32_t image_height = 0);
|
||||
BufferBlockAllocation request_scratch_buffer_memory(VkDeviceSize size);
|
||||
|
||||
void set_viewport(const VkViewport &viewport);
|
||||
const VkViewport &get_viewport() const;
|
||||
void set_scissor(const VkRect2D &rect);
|
||||
|
||||
void set_vertex_attrib(uint32_t attrib, uint32_t binding, VkFormat format, VkDeviceSize offset);
|
||||
void set_vertex_binding(uint32_t binding, const Buffer &buffer, VkDeviceSize offset, VkDeviceSize stride,
|
||||
VkVertexInputRate step_rate = VK_VERTEX_INPUT_RATE_VERTEX);
|
||||
void set_index_buffer(const Buffer &buffer, VkDeviceSize offset, VkIndexType index_type);
|
||||
|
||||
void draw(uint32_t vertex_count, uint32_t instance_count = 1, uint32_t first_vertex = 0,
|
||||
uint32_t first_instance = 0);
|
||||
void draw_indexed(uint32_t index_count, uint32_t instance_count = 1, uint32_t first_index = 0,
|
||||
int32_t vertex_offset = 0, uint32_t first_instance = 0);
|
||||
void draw_mesh_tasks(uint32_t tasks_x, uint32_t tasks_y, uint32_t tasks_z);
|
||||
|
||||
void dispatch(uint32_t groups_x, uint32_t groups_y, uint32_t groups_z);
|
||||
|
||||
void draw_indirect(const Buffer &buffer, VkDeviceSize offset, uint32_t draw_count, uint32_t stride);
|
||||
void draw_indexed_indirect(const Buffer &buffer, VkDeviceSize offset, uint32_t draw_count, uint32_t stride);
|
||||
void draw_multi_indirect(const Buffer &buffer, VkDeviceSize offset, uint32_t draw_count, uint32_t stride,
|
||||
const Buffer &count, VkDeviceSize count_offset);
|
||||
void draw_indexed_multi_indirect(const Buffer &buffer, VkDeviceSize offset, uint32_t draw_count, uint32_t stride,
|
||||
const Buffer &count, VkDeviceSize count_offset);
|
||||
void dispatch_indirect(const Buffer &buffer, VkDeviceSize offset);
|
||||
void draw_mesh_tasks_indirect(const Buffer &buffer, VkDeviceSize offset, uint32_t draw_count, uint32_t stride);
|
||||
void draw_mesh_tasks_multi_indirect(const Buffer &buffer, VkDeviceSize offset, uint32_t draw_count, uint32_t stride,
|
||||
const Buffer &count, VkDeviceSize count_offset);
|
||||
void execute_indirect_commands(const IndirectLayout *indirect_layout,
|
||||
uint32_t sequences,
|
||||
const Buffer &indirect, VkDeviceSize offset,
|
||||
const Buffer *count, size_t count_offset);
|
||||
|
||||
void set_opaque_state();
|
||||
void set_quad_state();
|
||||
void set_opaque_sprite_state();
|
||||
void set_transparent_sprite_state();
|
||||
|
||||
void save_state(CommandBufferSaveStateFlags flags, CommandBufferSavedState &state);
|
||||
void restore_state(const CommandBufferSavedState &state);
|
||||
|
||||
#define SET_STATIC_STATE(value) \
|
||||
do \
|
||||
{ \
|
||||
if (pipeline_state.static_state.state.value != value) \
|
||||
{ \
|
||||
pipeline_state.static_state.state.value = value; \
|
||||
set_dirty(COMMAND_BUFFER_DIRTY_STATIC_STATE_BIT); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define SET_POTENTIALLY_STATIC_STATE(value) \
|
||||
do \
|
||||
{ \
|
||||
if (pipeline_state.potential_static_state.value != value) \
|
||||
{ \
|
||||
pipeline_state.potential_static_state.value = value; \
|
||||
set_dirty(COMMAND_BUFFER_DIRTY_STATIC_STATE_BIT); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
inline void set_depth_test(bool depth_test, bool depth_write)
|
||||
{
|
||||
SET_STATIC_STATE(depth_test);
|
||||
SET_STATIC_STATE(depth_write);
|
||||
}
|
||||
|
||||
inline void set_wireframe(bool wireframe)
|
||||
{
|
||||
SET_STATIC_STATE(wireframe);
|
||||
}
|
||||
|
||||
inline void set_depth_compare(VkCompareOp depth_compare)
|
||||
{
|
||||
SET_STATIC_STATE(depth_compare);
|
||||
}
|
||||
|
||||
inline void set_blend_enable(bool blend_enable)
|
||||
{
|
||||
SET_STATIC_STATE(blend_enable);
|
||||
}
|
||||
|
||||
inline void set_blend_factors(VkBlendFactor src_color_blend, VkBlendFactor src_alpha_blend,
|
||||
VkBlendFactor dst_color_blend, VkBlendFactor dst_alpha_blend)
|
||||
{
|
||||
SET_STATIC_STATE(src_color_blend);
|
||||
SET_STATIC_STATE(dst_color_blend);
|
||||
SET_STATIC_STATE(src_alpha_blend);
|
||||
SET_STATIC_STATE(dst_alpha_blend);
|
||||
}
|
||||
|
||||
inline void set_blend_factors(VkBlendFactor src_blend, VkBlendFactor dst_blend)
|
||||
{
|
||||
set_blend_factors(src_blend, src_blend, dst_blend, dst_blend);
|
||||
}
|
||||
|
||||
inline void set_blend_op(VkBlendOp color_blend_op, VkBlendOp alpha_blend_op)
|
||||
{
|
||||
SET_STATIC_STATE(color_blend_op);
|
||||
SET_STATIC_STATE(alpha_blend_op);
|
||||
}
|
||||
|
||||
inline void set_blend_op(VkBlendOp blend_op)
|
||||
{
|
||||
set_blend_op(blend_op, blend_op);
|
||||
}
|
||||
|
||||
inline void set_depth_bias(bool depth_bias_enable)
|
||||
{
|
||||
SET_STATIC_STATE(depth_bias_enable);
|
||||
}
|
||||
|
||||
inline void set_color_write_mask(uint32_t write_mask)
|
||||
{
|
||||
SET_STATIC_STATE(write_mask);
|
||||
}
|
||||
|
||||
inline void set_stencil_test(bool stencil_test)
|
||||
{
|
||||
SET_STATIC_STATE(stencil_test);
|
||||
}
|
||||
|
||||
inline void set_stencil_front_ops(VkCompareOp stencil_front_compare_op, VkStencilOp stencil_front_pass,
|
||||
VkStencilOp stencil_front_fail, VkStencilOp stencil_front_depth_fail)
|
||||
{
|
||||
SET_STATIC_STATE(stencil_front_compare_op);
|
||||
SET_STATIC_STATE(stencil_front_pass);
|
||||
SET_STATIC_STATE(stencil_front_fail);
|
||||
SET_STATIC_STATE(stencil_front_depth_fail);
|
||||
}
|
||||
|
||||
inline void set_stencil_back_ops(VkCompareOp stencil_back_compare_op, VkStencilOp stencil_back_pass,
|
||||
VkStencilOp stencil_back_fail, VkStencilOp stencil_back_depth_fail)
|
||||
{
|
||||
SET_STATIC_STATE(stencil_back_compare_op);
|
||||
SET_STATIC_STATE(stencil_back_pass);
|
||||
SET_STATIC_STATE(stencil_back_fail);
|
||||
SET_STATIC_STATE(stencil_back_depth_fail);
|
||||
}
|
||||
|
||||
inline void set_stencil_ops(VkCompareOp stencil_compare_op, VkStencilOp stencil_pass, VkStencilOp stencil_fail,
|
||||
VkStencilOp stencil_depth_fail)
|
||||
{
|
||||
set_stencil_front_ops(stencil_compare_op, stencil_pass, stencil_fail, stencil_depth_fail);
|
||||
set_stencil_back_ops(stencil_compare_op, stencil_pass, stencil_fail, stencil_depth_fail);
|
||||
}
|
||||
|
||||
inline void set_primitive_topology(VkPrimitiveTopology topology)
|
||||
{
|
||||
SET_STATIC_STATE(topology);
|
||||
}
|
||||
|
||||
inline void set_primitive_restart(bool primitive_restart)
|
||||
{
|
||||
SET_STATIC_STATE(primitive_restart);
|
||||
}
|
||||
|
||||
inline void set_multisample_state(bool alpha_to_coverage, bool alpha_to_one = false, bool sample_shading = false)
|
||||
{
|
||||
SET_STATIC_STATE(alpha_to_coverage);
|
||||
SET_STATIC_STATE(alpha_to_one);
|
||||
SET_STATIC_STATE(sample_shading);
|
||||
}
|
||||
|
||||
inline void set_front_face(VkFrontFace front_face)
|
||||
{
|
||||
SET_STATIC_STATE(front_face);
|
||||
}
|
||||
|
||||
inline void set_cull_mode(VkCullModeFlags cull_mode)
|
||||
{
|
||||
SET_STATIC_STATE(cull_mode);
|
||||
}
|
||||
|
||||
inline void set_blend_constants(const float blend_constants[4])
|
||||
{
|
||||
SET_POTENTIALLY_STATIC_STATE(blend_constants[0]);
|
||||
SET_POTENTIALLY_STATIC_STATE(blend_constants[1]);
|
||||
SET_POTENTIALLY_STATIC_STATE(blend_constants[2]);
|
||||
SET_POTENTIALLY_STATIC_STATE(blend_constants[3]);
|
||||
}
|
||||
|
||||
inline void set_specialization_constant_mask(uint32_t spec_constant_mask)
|
||||
{
|
||||
VK_ASSERT((spec_constant_mask & ~((1u << VULKAN_NUM_USER_SPEC_CONSTANTS) - 1u)) == 0u);
|
||||
SET_POTENTIALLY_STATIC_STATE(spec_constant_mask);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void set_specialization_constant(unsigned index, const T &value)
|
||||
{
|
||||
VK_ASSERT(index < VULKAN_NUM_USER_SPEC_CONSTANTS);
|
||||
static_assert(sizeof(value) == sizeof(uint32_t), "Spec constant data must be 32-bit.");
|
||||
if (memcmp(&pipeline_state.potential_static_state.spec_constants[index], &value, sizeof(value)))
|
||||
{
|
||||
memcpy(&pipeline_state.potential_static_state.spec_constants[index], &value, sizeof(value));
|
||||
if (pipeline_state.potential_static_state.spec_constant_mask & (1u << index))
|
||||
set_dirty(COMMAND_BUFFER_DIRTY_STATIC_STATE_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
inline void set_specialization_constant(unsigned index, bool value)
|
||||
{
|
||||
set_specialization_constant(index, uint32_t(value));
|
||||
}
|
||||
|
||||
inline void enable_subgroup_size_control(bool subgroup_control_size,
|
||||
VkShaderStageFlagBits stage = VK_SHADER_STAGE_COMPUTE_BIT)
|
||||
{
|
||||
VK_ASSERT(stage == VK_SHADER_STAGE_TASK_BIT_EXT ||
|
||||
stage == VK_SHADER_STAGE_MESH_BIT_EXT ||
|
||||
stage == VK_SHADER_STAGE_COMPUTE_BIT);
|
||||
|
||||
if (stage != VK_SHADER_STAGE_TASK_BIT_EXT)
|
||||
{
|
||||
SET_STATIC_STATE(subgroup_control_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto subgroup_control_size_task = subgroup_control_size;
|
||||
SET_STATIC_STATE(subgroup_control_size_task);
|
||||
}
|
||||
}
|
||||
|
||||
inline void set_subgroup_size_log2(bool subgroup_full_group,
|
||||
uint8_t subgroup_minimum_size_log2,
|
||||
uint8_t subgroup_maximum_size_log2,
|
||||
VkShaderStageFlagBits stage = VK_SHADER_STAGE_COMPUTE_BIT)
|
||||
{
|
||||
VK_ASSERT(stage == VK_SHADER_STAGE_TASK_BIT_EXT ||
|
||||
stage == VK_SHADER_STAGE_MESH_BIT_EXT ||
|
||||
stage == VK_SHADER_STAGE_COMPUTE_BIT);
|
||||
|
||||
VK_ASSERT(subgroup_minimum_size_log2 < 8);
|
||||
VK_ASSERT(subgroup_maximum_size_log2 < 8);
|
||||
|
||||
if (stage != VK_SHADER_STAGE_TASK_BIT_EXT)
|
||||
{
|
||||
SET_STATIC_STATE(subgroup_full_group);
|
||||
SET_STATIC_STATE(subgroup_minimum_size_log2);
|
||||
SET_STATIC_STATE(subgroup_maximum_size_log2);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto subgroup_full_group_task = subgroup_full_group;
|
||||
auto subgroup_minimum_size_log2_task = subgroup_minimum_size_log2;
|
||||
auto subgroup_maximum_size_log2_task = subgroup_maximum_size_log2;
|
||||
SET_STATIC_STATE(subgroup_full_group_task);
|
||||
SET_STATIC_STATE(subgroup_minimum_size_log2_task);
|
||||
SET_STATIC_STATE(subgroup_maximum_size_log2_task);
|
||||
}
|
||||
}
|
||||
|
||||
inline void set_conservative_rasterization(bool conservative_raster)
|
||||
{
|
||||
SET_STATIC_STATE(conservative_raster);
|
||||
}
|
||||
|
||||
#define SET_DYNAMIC_STATE(state, flags) \
|
||||
do \
|
||||
{ \
|
||||
if (dynamic_state.state != state) \
|
||||
{ \
|
||||
dynamic_state.state = state; \
|
||||
set_dirty(flags); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
inline void set_depth_bias(float depth_bias_constant, float depth_bias_slope)
|
||||
{
|
||||
SET_DYNAMIC_STATE(depth_bias_constant, COMMAND_BUFFER_DIRTY_DEPTH_BIAS_BIT);
|
||||
SET_DYNAMIC_STATE(depth_bias_slope, COMMAND_BUFFER_DIRTY_DEPTH_BIAS_BIT);
|
||||
}
|
||||
|
||||
inline void set_stencil_front_reference(uint8_t front_compare_mask, uint8_t front_write_mask,
|
||||
uint8_t front_reference)
|
||||
{
|
||||
SET_DYNAMIC_STATE(front_compare_mask, COMMAND_BUFFER_DIRTY_STENCIL_REFERENCE_BIT);
|
||||
SET_DYNAMIC_STATE(front_write_mask, COMMAND_BUFFER_DIRTY_STENCIL_REFERENCE_BIT);
|
||||
SET_DYNAMIC_STATE(front_reference, COMMAND_BUFFER_DIRTY_STENCIL_REFERENCE_BIT);
|
||||
}
|
||||
|
||||
inline void set_stencil_back_reference(uint8_t back_compare_mask, uint8_t back_write_mask, uint8_t back_reference)
|
||||
{
|
||||
SET_DYNAMIC_STATE(back_compare_mask, COMMAND_BUFFER_DIRTY_STENCIL_REFERENCE_BIT);
|
||||
SET_DYNAMIC_STATE(back_write_mask, COMMAND_BUFFER_DIRTY_STENCIL_REFERENCE_BIT);
|
||||
SET_DYNAMIC_STATE(back_reference, COMMAND_BUFFER_DIRTY_STENCIL_REFERENCE_BIT);
|
||||
}
|
||||
|
||||
inline void set_stencil_reference(uint8_t compare_mask, uint8_t write_mask, uint8_t reference)
|
||||
{
|
||||
set_stencil_front_reference(compare_mask, write_mask, reference);
|
||||
set_stencil_back_reference(compare_mask, write_mask, reference);
|
||||
}
|
||||
|
||||
inline Type get_command_buffer_type() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
QueryPoolHandle write_timestamp(VkPipelineStageFlags2 stage);
|
||||
|
||||
// Used when recording command buffers in a thread, and submitting them in a different thread.
|
||||
// Need to make sure that no further commands on the VkCommandBuffer happen.
|
||||
void end_threaded_recording();
|
||||
// End is called automatically by Device in submission. Should not be called by application.
|
||||
void end();
|
||||
void enable_profiling();
|
||||
bool has_profiling() const;
|
||||
|
||||
void begin_debug_channel(DebugChannelInterface *iface, const char *tag, VkDeviceSize size);
|
||||
void end_debug_channel();
|
||||
|
||||
void extract_pipeline_state(DeferredPipelineCompile &compile) const;
|
||||
|
||||
enum class CompileMode
|
||||
{
|
||||
Sync,
|
||||
FailOnCompileRequired,
|
||||
AsyncThread,
|
||||
IndirectBindable
|
||||
};
|
||||
static Pipeline build_graphics_pipeline(Device *device, const DeferredPipelineCompile &compile, CompileMode mode);
|
||||
static Pipeline build_compute_pipeline(Device *device, const DeferredPipelineCompile &compile, CompileMode mode);
|
||||
bool flush_pipeline_state_without_blocking();
|
||||
|
||||
VkPipeline get_current_compute_pipeline();
|
||||
VkPipeline get_current_graphics_pipeline();
|
||||
|
||||
private:
|
||||
friend class Util::ObjectPool<CommandBuffer>;
|
||||
CommandBuffer(Device *device, VkCommandBuffer cmd, VkPipelineCache cache, Type type);
|
||||
|
||||
Device *device;
|
||||
const VolkDeviceTable &table;
|
||||
VkCommandBuffer cmd;
|
||||
Type type;
|
||||
|
||||
const Framebuffer *framebuffer = nullptr;
|
||||
const RenderPass *actual_render_pass = nullptr;
|
||||
const Vulkan::ImageView *framebuffer_attachments[VULKAN_NUM_ATTACHMENTS + 1] = {};
|
||||
|
||||
IndexState index_state = {};
|
||||
VertexBindingState vbo = {};
|
||||
ResourceBindings bindings;
|
||||
VkDescriptorSet bindless_sets[VULKAN_NUM_DESCRIPTOR_SETS] = {};
|
||||
VkDescriptorSet allocated_sets[VULKAN_NUM_DESCRIPTOR_SETS] = {};
|
||||
|
||||
Pipeline current_pipeline = {};
|
||||
VkPipelineLayout current_pipeline_layout = VK_NULL_HANDLE;
|
||||
VkSubpassContents current_contents = VK_SUBPASS_CONTENTS_INLINE;
|
||||
unsigned thread_index = 0;
|
||||
|
||||
VkViewport viewport = {};
|
||||
VkRect2D scissor = {};
|
||||
|
||||
CommandBufferDirtyFlags dirty = ~0u;
|
||||
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;
|
||||
bool is_compute = true;
|
||||
bool is_secondary = false;
|
||||
bool is_ended = false;
|
||||
bool framebuffer_is_multiview = false;
|
||||
|
||||
void set_dirty(CommandBufferDirtyFlags flags)
|
||||
{
|
||||
dirty |= flags;
|
||||
}
|
||||
|
||||
CommandBufferDirtyFlags get_and_clear(CommandBufferDirtyFlags flags)
|
||||
{
|
||||
auto mask = dirty & flags;
|
||||
dirty &= ~flags;
|
||||
return mask;
|
||||
}
|
||||
|
||||
DeferredPipelineCompile pipeline_state = {};
|
||||
DynamicState dynamic_state = {};
|
||||
#ifndef _MSC_VER
|
||||
static_assert(sizeof(pipeline_state.static_state.words) >= sizeof(pipeline_state.static_state.state),
|
||||
"Hashable pipeline state is not large enough!");
|
||||
#endif
|
||||
|
||||
VkPipeline flush_render_state(bool synchronous);
|
||||
VkPipeline flush_compute_state(bool synchronous);
|
||||
void clear_render_state();
|
||||
|
||||
bool flush_graphics_pipeline(bool synchronous);
|
||||
bool flush_compute_pipeline(bool synchronous);
|
||||
void flush_descriptor_sets();
|
||||
void begin_graphics();
|
||||
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();
|
||||
|
||||
BufferBlock vbo_block;
|
||||
BufferBlock ibo_block;
|
||||
BufferBlock ubo_block;
|
||||
BufferBlock staging_block;
|
||||
|
||||
void set_texture(unsigned set, unsigned binding, VkImageView float_view, VkImageView integer_view,
|
||||
VkImageLayout layout,
|
||||
uint64_t cookie);
|
||||
void set_buffer_view_common(unsigned set, unsigned binding, const BufferView &view);
|
||||
|
||||
void init_viewport_scissor(const RenderPassInfo &info, const Framebuffer *framebuffer);
|
||||
void init_surface_transform(const RenderPassInfo &info);
|
||||
VkSurfaceTransformFlagBitsKHR current_framebuffer_surface_transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
|
||||
bool profiling = false;
|
||||
std::string debug_channel_tag;
|
||||
Vulkan::BufferHandle debug_channel_buffer;
|
||||
DebugChannelInterface *debug_channel_interface = nullptr;
|
||||
|
||||
void bind_pipeline(VkPipelineBindPoint bind_point, VkPipeline pipeline, uint32_t active_dynamic_state);
|
||||
|
||||
static void update_hash_graphics_pipeline(DeferredPipelineCompile &compile, CompileMode mode, uint32_t *active_vbos);
|
||||
static void update_hash_compute_pipeline(DeferredPipelineCompile &compile);
|
||||
void set_surface_transform_specialization_constants();
|
||||
|
||||
void set_program_layout(const PipelineLayout *layout);
|
||||
|
||||
static bool setup_subgroup_size_control(Device &device, VkPipelineShaderStageCreateInfo &stage_info,
|
||||
VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT &required_info,
|
||||
VkShaderStageFlagBits stage,
|
||||
bool full_group, unsigned min_size_log2, unsigned max_size_log2);
|
||||
};
|
||||
|
||||
#ifdef GRANITE_VULKAN_SYSTEM_HANDLES
|
||||
struct CommandBufferUtil
|
||||
{
|
||||
static void draw_fullscreen_quad(CommandBuffer &cmd, const std::string &vertex, const std::string &fragment,
|
||||
const std::vector<std::pair<std::string, int>> &defines = {});
|
||||
static void draw_fullscreen_quad_depth(CommandBuffer &cmd, const std::string &vertex, const std::string &fragment,
|
||||
bool depth_test, bool depth_write, VkCompareOp depth_compare,
|
||||
const std::vector<std::pair<std::string, int>> &defines = {});
|
||||
static void set_fullscreen_quad_vertex_state(CommandBuffer &cmd);
|
||||
static void set_quad_vertex_state(CommandBuffer &cmd);
|
||||
|
||||
static void setup_fullscreen_quad(CommandBuffer &cmd, const std::string &vertex, const std::string &fragment,
|
||||
const std::vector<std::pair<std::string, int>> &defines = {},
|
||||
bool depth_test = false, bool depth_write = false,
|
||||
VkCompareOp depth_compare = VK_COMPARE_OP_ALWAYS);
|
||||
|
||||
static void draw_fullscreen_quad(CommandBuffer &cmd, unsigned instances = 1);
|
||||
static void draw_quad(CommandBuffer &cmd, unsigned instances = 1);
|
||||
};
|
||||
#endif
|
||||
|
||||
using CommandBufferHandle = Util::IntrusivePtr<CommandBuffer>;
|
||||
}
|
||||
180
external/parallel-rdp/parallel-rdp-standalone/vulkan/command_pool.cpp
vendored
Normal file
180
external/parallel-rdp/parallel-rdp-standalone/vulkan/command_pool.cpp
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "command_pool.hpp"
|
||||
#include "device.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
CommandPool::CommandPool(Device *device_, uint32_t queue_family_index)
|
||||
: device(device_), table(&device_->get_device_table())
|
||||
{
|
||||
VkCommandPoolCreateInfo info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
|
||||
info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
|
||||
info.queueFamilyIndex = queue_family_index;
|
||||
if (queue_family_index != VK_QUEUE_FAMILY_IGNORED)
|
||||
table->vkCreateCommandPool(device->get_device(), &info, nullptr, &pool);
|
||||
}
|
||||
|
||||
CommandPool::CommandPool(CommandPool &&other) noexcept
|
||||
{
|
||||
*this = std::move(other);
|
||||
}
|
||||
|
||||
CommandPool &CommandPool::operator=(CommandPool &&other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
device = other.device;
|
||||
table = other.table;
|
||||
if (!buffers.empty())
|
||||
table->vkFreeCommandBuffers(device->get_device(), pool, buffers.size(), buffers.data());
|
||||
if (pool != VK_NULL_HANDLE)
|
||||
table->vkDestroyCommandPool(device->get_device(), pool, nullptr);
|
||||
|
||||
pool = VK_NULL_HANDLE;
|
||||
buffers.clear();
|
||||
std::swap(pool, other.pool);
|
||||
std::swap(buffers, other.buffers);
|
||||
index = other.index;
|
||||
other.index = 0;
|
||||
#ifdef VULKAN_DEBUG
|
||||
in_flight.clear();
|
||||
std::swap(in_flight, other.in_flight);
|
||||
#endif
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
CommandPool::~CommandPool()
|
||||
{
|
||||
if (!buffers.empty())
|
||||
table->vkFreeCommandBuffers(device->get_device(), pool, buffers.size(), buffers.data());
|
||||
if (!secondary_buffers.empty())
|
||||
table->vkFreeCommandBuffers(device->get_device(), pool, secondary_buffers.size(), secondary_buffers.data());
|
||||
if (pool != VK_NULL_HANDLE)
|
||||
table->vkDestroyCommandPool(device->get_device(), pool, nullptr);
|
||||
}
|
||||
|
||||
void CommandPool::signal_submitted(VkCommandBuffer cmd)
|
||||
{
|
||||
#ifdef VULKAN_DEBUG
|
||||
VK_ASSERT(in_flight.find(cmd) != end(in_flight));
|
||||
in_flight.erase(cmd);
|
||||
#else
|
||||
(void)cmd;
|
||||
#endif
|
||||
}
|
||||
|
||||
VkCommandBuffer CommandPool::request_secondary_command_buffer()
|
||||
{
|
||||
VK_ASSERT(pool != VK_NULL_HANDLE);
|
||||
|
||||
if (secondary_index < secondary_buffers.size())
|
||||
{
|
||||
auto ret = secondary_buffers[secondary_index++];
|
||||
#ifdef VULKAN_DEBUG
|
||||
VK_ASSERT(in_flight.find(ret) == end(in_flight));
|
||||
in_flight.insert(ret);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
VkCommandBuffer cmd;
|
||||
VkCommandBufferAllocateInfo info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
|
||||
info.commandPool = pool;
|
||||
info.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
|
||||
info.commandBufferCount = 1;
|
||||
|
||||
table->vkAllocateCommandBuffers(device->get_device(), &info, &cmd);
|
||||
#ifdef VULKAN_DEBUG
|
||||
VK_ASSERT(in_flight.find(cmd) == end(in_flight));
|
||||
in_flight.insert(cmd);
|
||||
#endif
|
||||
secondary_buffers.push_back(cmd);
|
||||
secondary_index++;
|
||||
return cmd;
|
||||
}
|
||||
}
|
||||
|
||||
VkCommandBuffer CommandPool::request_command_buffer()
|
||||
{
|
||||
VK_ASSERT(pool != VK_NULL_HANDLE);
|
||||
|
||||
if (index < buffers.size())
|
||||
{
|
||||
auto ret = buffers[index++];
|
||||
#ifdef VULKAN_DEBUG
|
||||
VK_ASSERT(in_flight.find(ret) == end(in_flight));
|
||||
in_flight.insert(ret);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
VkCommandBuffer cmd;
|
||||
VkCommandBufferAllocateInfo info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
|
||||
info.commandPool = pool;
|
||||
info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
info.commandBufferCount = 1;
|
||||
|
||||
table->vkAllocateCommandBuffers(device->get_device(), &info, &cmd);
|
||||
#ifdef VULKAN_DEBUG
|
||||
VK_ASSERT(in_flight.find(cmd) == end(in_flight));
|
||||
in_flight.insert(cmd);
|
||||
#endif
|
||||
buffers.push_back(cmd);
|
||||
index++;
|
||||
return cmd;
|
||||
}
|
||||
}
|
||||
|
||||
void CommandPool::begin()
|
||||
{
|
||||
if (pool == VK_NULL_HANDLE)
|
||||
return;
|
||||
|
||||
#ifdef VULKAN_DEBUG
|
||||
VK_ASSERT(in_flight.empty());
|
||||
#endif
|
||||
if (index > 0 || secondary_index > 0)
|
||||
table->vkResetCommandPool(device->get_device(), pool, 0);
|
||||
index = 0;
|
||||
secondary_index = 0;
|
||||
}
|
||||
|
||||
void CommandPool::trim()
|
||||
{
|
||||
if (pool == VK_NULL_HANDLE)
|
||||
return;
|
||||
|
||||
table->vkResetCommandPool(device->get_device(), pool, VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT);
|
||||
if (!buffers.empty())
|
||||
table->vkFreeCommandBuffers(device->get_device(), pool, buffers.size(), buffers.data());
|
||||
if (!secondary_buffers.empty())
|
||||
table->vkFreeCommandBuffers(device->get_device(), pool, secondary_buffers.size(), secondary_buffers.data());
|
||||
buffers.clear();
|
||||
secondary_buffers.clear();
|
||||
table->vkTrimCommandPool(device->get_device(), pool, 0);
|
||||
}
|
||||
}
|
||||
61
external/parallel-rdp/parallel-rdp-standalone/vulkan/command_pool.hpp
vendored
Normal file
61
external/parallel-rdp/parallel-rdp-standalone/vulkan/command_pool.hpp
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vulkan_headers.hpp"
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class Device;
|
||||
class CommandPool
|
||||
{
|
||||
public:
|
||||
CommandPool(Device *device, uint32_t queue_family_index);
|
||||
~CommandPool();
|
||||
|
||||
CommandPool(CommandPool &&) noexcept;
|
||||
CommandPool &operator=(CommandPool &&) noexcept;
|
||||
CommandPool(const CommandPool &) = delete;
|
||||
void operator=(const CommandPool &) = delete;
|
||||
|
||||
void begin();
|
||||
void trim();
|
||||
VkCommandBuffer request_command_buffer();
|
||||
VkCommandBuffer request_secondary_command_buffer();
|
||||
void signal_submitted(VkCommandBuffer cmd);
|
||||
|
||||
private:
|
||||
Device *device;
|
||||
const VolkDeviceTable *table;
|
||||
VkCommandPool pool = VK_NULL_HANDLE;
|
||||
std::vector<VkCommandBuffer> buffers;
|
||||
std::vector<VkCommandBuffer> secondary_buffers;
|
||||
#ifdef VULKAN_DEBUG
|
||||
std::unordered_set<VkCommandBuffer> in_flight;
|
||||
#endif
|
||||
unsigned index = 0;
|
||||
unsigned secondary_index = 0;
|
||||
};
|
||||
}
|
||||
1779
external/parallel-rdp/parallel-rdp-standalone/vulkan/context.cpp
vendored
Normal file
1779
external/parallel-rdp/parallel-rdp-standalone/vulkan/context.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
402
external/parallel-rdp/parallel-rdp-standalone/vulkan/context.hpp
vendored
Normal file
402
external/parallel-rdp/parallel-rdp-standalone/vulkan/context.hpp
vendored
Normal file
@@ -0,0 +1,402 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vulkan_headers.hpp"
|
||||
#include "logging.hpp"
|
||||
#include "vulkan_common.hpp"
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
#ifdef GRANITE_VULKAN_FOSSILIZE
|
||||
#include "cli/fossilize_feature_filter.hpp"
|
||||
#endif
|
||||
|
||||
namespace Util
|
||||
{
|
||||
class TimelineTraceFile;
|
||||
}
|
||||
|
||||
namespace Granite
|
||||
{
|
||||
class Filesystem;
|
||||
class ThreadGroup;
|
||||
class AssetManager;
|
||||
}
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
struct DeviceFeatures
|
||||
{
|
||||
bool supports_debug_utils = false;
|
||||
bool supports_external_memory_host = false;
|
||||
bool supports_surface_capabilities2 = false;
|
||||
bool supports_full_screen_exclusive = false;
|
||||
bool supports_conservative_rasterization = false;
|
||||
bool supports_calibrated_timestamps = false;
|
||||
bool supports_memory_budget = false;
|
||||
bool supports_video_queue = false;
|
||||
bool supports_driver_properties = false;
|
||||
bool supports_video_decode_queue = false;
|
||||
bool supports_video_decode_h264 = false;
|
||||
bool supports_video_decode_h265 = false;
|
||||
bool supports_astc_decode_mode = false;
|
||||
bool supports_image_format_list = false;
|
||||
bool supports_format_feature_flags2 = false;
|
||||
bool supports_video_encode_queue = false;
|
||||
bool supports_video_encode_h264 = false;
|
||||
bool supports_video_encode_h265 = false;
|
||||
bool supports_external = false;
|
||||
bool supports_tooling_info = false;
|
||||
bool supports_hdr_metadata = false;
|
||||
bool supports_swapchain_colorspace = false;
|
||||
bool supports_surface_maintenance1 = false;
|
||||
bool supports_store_op_none = false;
|
||||
bool supports_push_descriptor = false;
|
||||
|
||||
VkPhysicalDeviceFeatures enabled_features = {};
|
||||
|
||||
VkPhysicalDeviceVulkan11Features vk11_features = {};
|
||||
VkPhysicalDeviceVulkan12Features vk12_features = {};
|
||||
VkPhysicalDeviceVulkan13Features vk13_features = {};
|
||||
VkPhysicalDeviceVulkan11Properties vk11_props = {};
|
||||
VkPhysicalDeviceVulkan12Properties vk12_props = {};
|
||||
VkPhysicalDeviceVulkan13Properties vk13_props = {};
|
||||
|
||||
// KHR
|
||||
VkPhysicalDevicePerformanceQueryFeaturesKHR performance_query_features = {};
|
||||
VkPhysicalDevicePresentIdFeaturesKHR present_id_features = {};
|
||||
VkPhysicalDevicePresentWaitFeaturesKHR present_wait_features = {};
|
||||
VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR barycentric_features = {};
|
||||
VkPhysicalDeviceVideoMaintenance1FeaturesKHR video_maintenance1_features = {};
|
||||
|
||||
// EXT
|
||||
VkPhysicalDeviceExternalMemoryHostPropertiesEXT host_memory_properties = {};
|
||||
VkPhysicalDeviceConservativeRasterizationPropertiesEXT conservative_rasterization_properties = {};
|
||||
VkPhysicalDeviceMemoryPriorityFeaturesEXT memory_priority_features = {};
|
||||
VkPhysicalDeviceASTCDecodeFeaturesEXT astc_decode_features = {};
|
||||
VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT swapchain_maintenance1_features = {};
|
||||
VkPhysicalDevicePageableDeviceLocalMemoryFeaturesEXT pageable_device_local_memory_features = {};
|
||||
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 = {};
|
||||
VkPhysicalDevice16BitStorageFeaturesKHR storage_16bit_features = {};
|
||||
// Fallback feature structs (Vulkan 1.2)
|
||||
VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8_features = {};
|
||||
VkPhysicalDevice8BitStorageFeaturesKHR storage_8bit_features = {};
|
||||
// Fallback feature structs (Vulkan 1.3)
|
||||
VkPhysicalDeviceSubgroupSizeControlFeatures subgroup_size_control_features = {};
|
||||
|
||||
VkDriverId driver_id = {};
|
||||
|
||||
// References Vulkan::Context.
|
||||
const VkPhysicalDeviceFeatures2 *pdf2 = nullptr;
|
||||
const char * const * instance_extensions = nullptr;
|
||||
uint32_t num_instance_extensions = 0;
|
||||
const char * const * device_extensions = nullptr;
|
||||
uint32_t num_device_extensions = 0;
|
||||
|
||||
uint32_t instance_api_core_version = VK_API_VERSION_1_1;
|
||||
uint32_t device_api_core_version = VK_API_VERSION_1_1;
|
||||
};
|
||||
|
||||
enum VendorID
|
||||
{
|
||||
VENDOR_ID_AMD = 0x1002,
|
||||
VENDOR_ID_NVIDIA = 0x10de,
|
||||
VENDOR_ID_INTEL = 0x8086,
|
||||
VENDOR_ID_ARM = 0x13b5,
|
||||
VENDOR_ID_QCOM = 0x5143
|
||||
};
|
||||
|
||||
enum ContextCreationFlagBits
|
||||
{
|
||||
CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT = 1 << 0,
|
||||
CONTEXT_CREATION_ENABLE_VIDEO_DECODE_BIT = 1 << 1,
|
||||
CONTEXT_CREATION_ENABLE_VIDEO_ENCODE_BIT = 1 << 2,
|
||||
CONTEXT_CREATION_ENABLE_VIDEO_H264_BIT = 1 << 3,
|
||||
CONTEXT_CREATION_ENABLE_VIDEO_H265_BIT = 1 << 4
|
||||
};
|
||||
using ContextCreationFlags = uint32_t;
|
||||
|
||||
struct QueueInfo
|
||||
{
|
||||
QueueInfo();
|
||||
VkQueue queues[QUEUE_INDEX_COUNT] = {};
|
||||
uint32_t family_indices[QUEUE_INDEX_COUNT];
|
||||
uint32_t counts[QUEUE_INDEX_COUNT] = {};
|
||||
uint32_t timestamp_valid_bits = 0;
|
||||
};
|
||||
|
||||
struct InstanceFactory
|
||||
{
|
||||
virtual ~InstanceFactory() = default;
|
||||
virtual VkInstance create_instance(const VkInstanceCreateInfo *info) = 0;
|
||||
};
|
||||
|
||||
struct DeviceFactory
|
||||
{
|
||||
virtual ~DeviceFactory() = default;
|
||||
virtual VkDevice create_device(VkPhysicalDevice gpu, const VkDeviceCreateInfo *info) = 0;
|
||||
};
|
||||
|
||||
class CopiedApplicationInfo
|
||||
{
|
||||
public:
|
||||
CopiedApplicationInfo();
|
||||
const VkApplicationInfo &get_application_info() const;
|
||||
void copy_assign(const VkApplicationInfo *info);
|
||||
|
||||
private:
|
||||
std::string application;
|
||||
std::string engine;
|
||||
VkApplicationInfo app = {
|
||||
VK_STRUCTURE_TYPE_APPLICATION_INFO, nullptr, "Granite", 0, "Granite", 0, VK_API_VERSION_1_1,
|
||||
};
|
||||
|
||||
void set_default_app();
|
||||
};
|
||||
|
||||
class Context
|
||||
: public Util::IntrusivePtrEnabled<Context, std::default_delete<Context>, HandleCounter>
|
||||
#ifdef GRANITE_VULKAN_FOSSILIZE
|
||||
, public Fossilize::DeviceQueryInterface
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
// If these interface are set, factory->create() calls are used instead of global vkCreateInstance and vkCreateDevice.
|
||||
// For deeper API interop scenarios.
|
||||
void set_instance_factory(InstanceFactory *factory);
|
||||
void set_device_factory(DeviceFactory *factory);
|
||||
|
||||
// Only takes effect if profiles are enabled in build. (GRANITE_VULKAN_PROFILES)
|
||||
// If profile is non-null, forces a specific profile.
|
||||
// If not supported, initialization fails.
|
||||
// If not set, ignore profiles.
|
||||
// If strict is false, the profile should be seen as a baseline and Granite will augment features on top.
|
||||
// If true, the profile is a strict limit for device functionality. For validation purposes.
|
||||
void set_required_profile(const char *profile, bool strict);
|
||||
|
||||
// Call before initializing instances. app_info may be freed after returning.
|
||||
// API_VERSION must be at least 1.1.
|
||||
// By default, a Vulkan 1.1 instance is created.
|
||||
void set_application_info(const VkApplicationInfo *app_info);
|
||||
|
||||
// Recommended interface.
|
||||
// InstanceFactory can be used to override enabled instance layers and extensions.
|
||||
// For simple WSI use, it is enough to just enable VK_KHR_surface and the platform.
|
||||
bool init_instance(const char * const *instance_ext, uint32_t instance_ext_count,
|
||||
ContextCreationFlags flags = 0);
|
||||
// DeviceFactory can be used to override enabled device extensions.
|
||||
// For simple WSI use, it is enough to just enable VK_KHR_swapchain.
|
||||
bool init_device(VkPhysicalDevice gpu, VkSurfaceKHR surface_compat,
|
||||
const char * const *device_ext, uint32_t device_ext_count,
|
||||
ContextCreationFlags flags = 0);
|
||||
|
||||
// Simplified initialization which calls init_instance and init_device in succession with NULL GPU and surface.
|
||||
// Provided for compat with older code.
|
||||
bool init_instance_and_device(const char * const *instance_ext, uint32_t instance_ext_count,
|
||||
const char * const *device_ext, uint32_t device_ext_count,
|
||||
ContextCreationFlags flags = 0);
|
||||
|
||||
// Deprecated. For libretro Vulkan context negotiation v1.
|
||||
// Use InstanceFactory and DeviceFactory for more advanced scenarios in v2.
|
||||
bool init_device_from_instance(VkInstance instance, VkPhysicalDevice gpu, VkSurfaceKHR surface,
|
||||
const char **required_device_extensions,
|
||||
unsigned num_required_device_extensions,
|
||||
const VkPhysicalDeviceFeatures *required_features,
|
||||
ContextCreationFlags flags = 0);
|
||||
|
||||
Context();
|
||||
Context(const Context &) = delete;
|
||||
void operator=(const Context &) = delete;
|
||||
static bool init_loader(PFN_vkGetInstanceProcAddr addr, bool force_reload = false);
|
||||
static PFN_vkGetInstanceProcAddr get_instance_proc_addr();
|
||||
|
||||
~Context();
|
||||
|
||||
VkInstance get_instance() const
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
VkPhysicalDevice get_gpu() const
|
||||
{
|
||||
return gpu;
|
||||
}
|
||||
|
||||
VkDevice get_device() const
|
||||
{
|
||||
return device;
|
||||
}
|
||||
|
||||
const QueueInfo &get_queue_info() const
|
||||
{
|
||||
return queue_info;
|
||||
}
|
||||
|
||||
const VkPhysicalDeviceProperties &get_gpu_props() const
|
||||
{
|
||||
return gpu_props;
|
||||
}
|
||||
|
||||
const VkPhysicalDeviceMemoryProperties &get_mem_props() const
|
||||
{
|
||||
return mem_props;
|
||||
}
|
||||
|
||||
void release_instance()
|
||||
{
|
||||
owned_instance = false;
|
||||
}
|
||||
|
||||
void release_device()
|
||||
{
|
||||
owned_device = false;
|
||||
}
|
||||
|
||||
const DeviceFeatures &get_enabled_device_features() const
|
||||
{
|
||||
return ext;
|
||||
}
|
||||
|
||||
const VkApplicationInfo &get_application_info() const;
|
||||
|
||||
void notify_validation_error(const char *msg);
|
||||
void set_notification_callback(std::function<void (const char *)> func);
|
||||
|
||||
void set_num_thread_indices(unsigned indices)
|
||||
{
|
||||
num_thread_indices = indices;
|
||||
}
|
||||
|
||||
unsigned get_num_thread_indices() const
|
||||
{
|
||||
return num_thread_indices;
|
||||
}
|
||||
|
||||
const VolkDeviceTable &get_device_table() const
|
||||
{
|
||||
return device_table;
|
||||
}
|
||||
|
||||
struct SystemHandles
|
||||
{
|
||||
Util::TimelineTraceFile *timeline_trace_file = nullptr;
|
||||
Granite::Filesystem *filesystem = nullptr;
|
||||
Granite::ThreadGroup *thread_group = nullptr;
|
||||
Granite::AssetManager *asset_manager = nullptr;
|
||||
};
|
||||
|
||||
void set_system_handles(const SystemHandles &handles_)
|
||||
{
|
||||
handles = handles_;
|
||||
}
|
||||
|
||||
const SystemHandles &get_system_handles() const
|
||||
{
|
||||
return handles;
|
||||
}
|
||||
|
||||
#ifdef GRANITE_VULKAN_FOSSILIZE
|
||||
const Fossilize::FeatureFilter &get_feature_filter() const
|
||||
{
|
||||
return feature_filter;
|
||||
}
|
||||
#endif
|
||||
|
||||
const VkPhysicalDeviceFeatures2 &get_physical_device_features() const
|
||||
{
|
||||
return pdf2;
|
||||
}
|
||||
|
||||
private:
|
||||
InstanceFactory *instance_factory = nullptr;
|
||||
DeviceFactory *device_factory = nullptr;
|
||||
VkDevice device = VK_NULL_HANDLE;
|
||||
VkInstance instance = VK_NULL_HANDLE;
|
||||
VkPhysicalDevice gpu = VK_NULL_HANDLE;
|
||||
VolkDeviceTable device_table = {};
|
||||
SystemHandles handles;
|
||||
VkPhysicalDeviceProperties gpu_props = {};
|
||||
VkPhysicalDeviceMemoryProperties mem_props = {};
|
||||
|
||||
CopiedApplicationInfo user_application_info;
|
||||
|
||||
QueueInfo queue_info;
|
||||
unsigned num_thread_indices = 1;
|
||||
|
||||
bool create_instance(const char * const *instance_ext, uint32_t instance_ext_count, ContextCreationFlags flags);
|
||||
bool create_device(VkPhysicalDevice gpu, VkSurfaceKHR surface,
|
||||
const char * const *required_device_extensions, uint32_t num_required_device_extensions,
|
||||
const VkPhysicalDeviceFeatures *required_features, ContextCreationFlags flags);
|
||||
|
||||
bool owned_instance = false;
|
||||
bool owned_device = false;
|
||||
DeviceFeatures ext;
|
||||
VkPhysicalDeviceFeatures2 pdf2;
|
||||
std::vector<const char *> enabled_device_extensions;
|
||||
std::vector<const char *> enabled_instance_extensions;
|
||||
|
||||
std::string required_profile;
|
||||
bool required_profile_strict = false;
|
||||
|
||||
#ifdef VULKAN_DEBUG
|
||||
VkDebugUtilsMessengerEXT debug_messenger = VK_NULL_HANDLE;
|
||||
bool force_no_validation = false;
|
||||
#endif
|
||||
std::function<void (const char *)> message_callback;
|
||||
|
||||
void destroy_instance();
|
||||
void destroy_device();
|
||||
|
||||
bool physical_device_supports_surface_and_profile(VkPhysicalDevice candidate_gpu, VkSurfaceKHR surface) const;
|
||||
|
||||
#ifdef GRANITE_VULKAN_FOSSILIZE
|
||||
Fossilize::FeatureFilter feature_filter;
|
||||
bool format_is_supported(VkFormat format, VkFormatFeatureFlags features) override;
|
||||
bool descriptor_set_layout_is_supported(const VkDescriptorSetLayoutCreateInfo *set_layout) override;
|
||||
#endif
|
||||
|
||||
bool init_profile();
|
||||
VkResult create_instance_from_profile(const VkInstanceCreateInfo &info, VkInstance *pInstance);
|
||||
VkResult create_device_from_profile(const VkDeviceCreateInfo &info, VkDevice *pDevice);
|
||||
|
||||
VkApplicationInfo get_promoted_application_info() const;
|
||||
};
|
||||
|
||||
using ContextHandle = Util::IntrusivePtr<Context>;
|
||||
}
|
||||
32
external/parallel-rdp/parallel-rdp-standalone/vulkan/cookie.cpp
vendored
Normal file
32
external/parallel-rdp/parallel-rdp-standalone/vulkan/cookie.cpp
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "cookie.hpp"
|
||||
#include "device.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
Cookie::Cookie(Device *device)
|
||||
: cookie(device->allocate_cookie())
|
||||
{
|
||||
}
|
||||
}
|
||||
61
external/parallel-rdp/parallel-rdp-standalone/vulkan/cookie.hpp
vendored
Normal file
61
external/parallel-rdp/parallel-rdp-standalone/vulkan/cookie.hpp
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "hash.hpp"
|
||||
#include "intrusive_hash_map.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class Device;
|
||||
|
||||
class Cookie
|
||||
{
|
||||
public:
|
||||
Cookie(Device *device);
|
||||
|
||||
uint64_t get_cookie() const
|
||||
{
|
||||
return cookie;
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t cookie;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using HashedObject = Util::IntrusiveHashMapEnabled<T>;
|
||||
|
||||
class InternalSyncEnabled
|
||||
{
|
||||
public:
|
||||
void set_internal_sync_object()
|
||||
{
|
||||
internal_sync = true;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool internal_sync = false;
|
||||
};
|
||||
}
|
||||
521
external/parallel-rdp/parallel-rdp-standalone/vulkan/descriptor_set.cpp
vendored
Normal file
521
external/parallel-rdp/parallel-rdp-standalone/vulkan/descriptor_set.cpp
vendored
Normal file
@@ -0,0 +1,521 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include "descriptor_set.hpp"
|
||||
#include "device.hpp"
|
||||
#include <vector>
|
||||
|
||||
using namespace Util;
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
DescriptorSetAllocator::DescriptorSetAllocator(Hash hash, Device *device_, const DescriptorSetLayout &layout,
|
||||
const uint32_t *stages_for_binds,
|
||||
const ImmutableSampler * const *immutable_samplers)
|
||||
: IntrusiveHashMapEnabled<DescriptorSetAllocator>(hash)
|
||||
, device(device_)
|
||||
, table(device_->get_device_table())
|
||||
{
|
||||
bindless = layout.array_size[0] == DescriptorSetLayout::UNSIZED_ARRAY;
|
||||
|
||||
if (!bindless)
|
||||
{
|
||||
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)
|
||||
{
|
||||
LOGE("Cannot support descriptor indexing on this device.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
|
||||
VkDescriptorSetLayoutBindingFlagsCreateInfoEXT flags = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT };
|
||||
VkSampler vk_immutable_samplers[VULKAN_NUM_BINDINGS] = {};
|
||||
std::vector<VkDescriptorSetLayoutBinding> bindings;
|
||||
VkDescriptorBindingFlagsEXT binding_flags = 0;
|
||||
|
||||
if (bindless)
|
||||
{
|
||||
info.flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT;
|
||||
info.pNext = &flags;
|
||||
|
||||
flags.bindingCount = 1;
|
||||
flags.pBindingFlags = &binding_flags;
|
||||
binding_flags = VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT |
|
||||
VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT |
|
||||
VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < VULKAN_NUM_BINDINGS; i++)
|
||||
{
|
||||
auto stages = stages_for_binds[i];
|
||||
if (stages == 0)
|
||||
continue;
|
||||
|
||||
unsigned array_size = layout.array_size[i];
|
||||
unsigned pool_array_size;
|
||||
if (array_size == DescriptorSetLayout::UNSIZED_ARRAY)
|
||||
{
|
||||
array_size = VULKAN_NUM_BINDINGS_BINDLESS_VARYING;
|
||||
pool_array_size = array_size;
|
||||
}
|
||||
else
|
||||
pool_array_size = array_size * VULKAN_NUM_SETS_PER_POOL;
|
||||
|
||||
unsigned types = 0;
|
||||
if (layout.sampled_image_mask & (1u << i))
|
||||
{
|
||||
if ((layout.immutable_sampler_mask & (1u << i)) && immutable_samplers && immutable_samplers[i])
|
||||
vk_immutable_samplers[i] = immutable_samplers[i]->get_sampler().get_sampler();
|
||||
|
||||
bindings.push_back({ i, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, array_size, stages,
|
||||
vk_immutable_samplers[i] != VK_NULL_HANDLE ? &vk_immutable_samplers[i] : nullptr });
|
||||
pool_size.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, pool_array_size });
|
||||
types++;
|
||||
}
|
||||
|
||||
if (layout.sampled_texel_buffer_mask & (1u << i))
|
||||
{
|
||||
bindings.push_back({ i, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, array_size, stages, nullptr });
|
||||
pool_size.push_back({ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, pool_array_size });
|
||||
types++;
|
||||
}
|
||||
|
||||
if (layout.storage_texel_buffer_mask & (1u << i))
|
||||
{
|
||||
bindings.push_back({ i, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, array_size, stages, nullptr });
|
||||
pool_size.push_back({ VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, pool_array_size });
|
||||
types++;
|
||||
}
|
||||
|
||||
if (layout.storage_image_mask & (1u << i))
|
||||
{
|
||||
bindings.push_back({ i, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, array_size, stages, nullptr });
|
||||
pool_size.push_back({ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, pool_array_size });
|
||||
types++;
|
||||
}
|
||||
|
||||
if (layout.uniform_buffer_mask & (1u << i))
|
||||
{
|
||||
bindings.push_back({ i, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, array_size, stages, nullptr });
|
||||
pool_size.push_back({ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, pool_array_size });
|
||||
types++;
|
||||
}
|
||||
|
||||
if (layout.storage_buffer_mask & (1u << i))
|
||||
{
|
||||
bindings.push_back({ i, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, array_size, stages, nullptr });
|
||||
pool_size.push_back({ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, pool_array_size });
|
||||
types++;
|
||||
}
|
||||
|
||||
if (layout.input_attachment_mask & (1u << i))
|
||||
{
|
||||
bindings.push_back({ i, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, array_size, stages, nullptr });
|
||||
pool_size.push_back({ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, pool_array_size });
|
||||
types++;
|
||||
}
|
||||
|
||||
if (layout.separate_image_mask & (1u << i))
|
||||
{
|
||||
bindings.push_back({ i, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, array_size, stages, nullptr });
|
||||
pool_size.push_back({ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, pool_array_size });
|
||||
types++;
|
||||
}
|
||||
|
||||
if (layout.sampler_mask & (1u << i))
|
||||
{
|
||||
if ((layout.immutable_sampler_mask & (1u << i)) && immutable_samplers && immutable_samplers[i])
|
||||
vk_immutable_samplers[i] = immutable_samplers[i]->get_sampler().get_sampler();
|
||||
|
||||
bindings.push_back({ i, VK_DESCRIPTOR_TYPE_SAMPLER, array_size, stages,
|
||||
vk_immutable_samplers[i] != VK_NULL_HANDLE ? &vk_immutable_samplers[i] : nullptr });
|
||||
pool_size.push_back({ VK_DESCRIPTOR_TYPE_SAMPLER, pool_array_size });
|
||||
types++;
|
||||
}
|
||||
|
||||
(void)types;
|
||||
VK_ASSERT(types <= 1 && "Descriptor set aliasing!");
|
||||
}
|
||||
|
||||
if (!bindings.empty())
|
||||
{
|
||||
info.bindingCount = bindings.size();
|
||||
info.pBindings = bindings.data();
|
||||
|
||||
if (bindless && bindings.size() != 1)
|
||||
{
|
||||
LOGE("Using bindless but have bindingCount != 1.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef VULKAN_DEBUG
|
||||
LOGI("Creating descriptor set layout.\n");
|
||||
#endif
|
||||
if (table.vkCreateDescriptorSetLayout(device->get_device(), &info, nullptr, &set_layout_pool) != VK_SUCCESS)
|
||||
LOGE("Failed to create descriptor set layout.");
|
||||
|
||||
#ifdef GRANITE_VULKAN_FOSSILIZE
|
||||
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 && !device->workarounds.broken_push_descriptors)
|
||||
{
|
||||
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)
|
||||
{
|
||||
table.vkResetDescriptorPool(device->get_device(), pool, 0);
|
||||
}
|
||||
|
||||
VkDescriptorSet DescriptorSetAllocator::allocate_bindless_set(VkDescriptorPool pool, unsigned num_descriptors)
|
||||
{
|
||||
if (!pool || !bindless)
|
||||
return VK_NULL_HANDLE;
|
||||
|
||||
VkDescriptorSetAllocateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
|
||||
info.descriptorPool = pool;
|
||||
info.descriptorSetCount = 1;
|
||||
info.pSetLayouts = &set_layout_pool;
|
||||
|
||||
VkDescriptorSetVariableDescriptorCountAllocateInfoEXT count_info =
|
||||
{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO_EXT };
|
||||
|
||||
uint32_t num_desc = num_descriptors;
|
||||
count_info.descriptorSetCount = 1;
|
||||
count_info.pDescriptorCounts = &num_desc;
|
||||
info.pNext = &count_info;
|
||||
|
||||
VkDescriptorSet desc_set = VK_NULL_HANDLE;
|
||||
if (table.vkAllocateDescriptorSets(device->get_device(), &info, &desc_set) != VK_SUCCESS)
|
||||
return VK_NULL_HANDLE;
|
||||
|
||||
return desc_set;
|
||||
}
|
||||
|
||||
VkDescriptorPool DescriptorSetAllocator::allocate_bindless_pool(unsigned num_sets, unsigned num_descriptors)
|
||||
{
|
||||
if (!bindless)
|
||||
return VK_NULL_HANDLE;
|
||||
|
||||
VkDescriptorPool pool = VK_NULL_HANDLE;
|
||||
VkDescriptorPoolCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
|
||||
info.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT_EXT;
|
||||
info.maxSets = num_sets;
|
||||
info.poolSizeCount = 1;
|
||||
|
||||
VkDescriptorPoolSize size = pool_size[0];
|
||||
size.descriptorCount = num_descriptors;
|
||||
info.pPoolSizes = &size;
|
||||
|
||||
if (table.vkCreateDescriptorPool(device->get_device(), &info, nullptr, &pool) != VK_SUCCESS)
|
||||
{
|
||||
LOGE("Failed to create descriptor pool.\n");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
void DescriptorSetAllocator::begin_frame()
|
||||
{
|
||||
if (!bindless)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
VkDescriptorSet DescriptorSetAllocator::request_descriptor_set(unsigned thread_index, unsigned frame_index)
|
||||
{
|
||||
VK_ASSERT(!bindless);
|
||||
|
||||
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())
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
VkDescriptorSet vk_set = state.pools[pool_index]->sets[pool_offset];
|
||||
state.offset++;
|
||||
return vk_set;
|
||||
}
|
||||
|
||||
void DescriptorSetAllocator::clear()
|
||||
{
|
||||
for (auto &state : per_thread_and_frame)
|
||||
{
|
||||
for (auto *obj : state.pools)
|
||||
{
|
||||
table.vkDestroyDescriptorPool(device->get_device(), obj->pool, nullptr);
|
||||
state.object_pool.free(obj);
|
||||
}
|
||||
state.pools.clear();
|
||||
state.offset = 0;
|
||||
state.object_pool = {};
|
||||
}
|
||||
}
|
||||
|
||||
DescriptorSetAllocator::~DescriptorSetAllocator()
|
||||
{
|
||||
table.vkDestroyDescriptorSetLayout(device->get_device(), set_layout_pool, nullptr);
|
||||
table.vkDestroyDescriptorSetLayout(device->get_device(), set_layout_push, nullptr);
|
||||
clear();
|
||||
}
|
||||
|
||||
BindlessDescriptorPool::BindlessDescriptorPool(Device *device_, DescriptorSetAllocator *allocator_,
|
||||
VkDescriptorPool pool, uint32_t num_sets, uint32_t num_desc)
|
||||
: device(device_), allocator(allocator_), desc_pool(pool), total_sets(num_sets), total_descriptors(num_desc)
|
||||
{
|
||||
}
|
||||
|
||||
BindlessDescriptorPool::~BindlessDescriptorPool()
|
||||
{
|
||||
if (desc_pool)
|
||||
{
|
||||
if (internal_sync)
|
||||
device->destroy_descriptor_pool_nolock(desc_pool);
|
||||
else
|
||||
device->destroy_descriptor_pool(desc_pool);
|
||||
}
|
||||
}
|
||||
|
||||
VkDescriptorSet BindlessDescriptorPool::get_descriptor_set() const
|
||||
{
|
||||
return desc_set;
|
||||
}
|
||||
|
||||
void BindlessDescriptorPool::reset()
|
||||
{
|
||||
if (desc_pool != VK_NULL_HANDLE)
|
||||
allocator->reset_bindless_pool(desc_pool);
|
||||
desc_set = VK_NULL_HANDLE;
|
||||
allocated_descriptor_count = 0;
|
||||
allocated_sets = 0;
|
||||
}
|
||||
|
||||
bool BindlessDescriptorPool::allocate_descriptors(unsigned count)
|
||||
{
|
||||
// Not all drivers will exhaust the pool for us, so make sure we don't allocate more than expected.
|
||||
if (allocated_sets == total_sets)
|
||||
return false;
|
||||
if (allocated_descriptor_count + count > total_descriptors)
|
||||
return false;
|
||||
|
||||
allocated_descriptor_count += count;
|
||||
allocated_sets++;
|
||||
|
||||
desc_set = allocator->allocate_bindless_set(desc_pool, count);
|
||||
|
||||
infos.reserve(count);
|
||||
write_count = 0;
|
||||
|
||||
return desc_set != VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void BindlessDescriptorPool::push_texture(const ImageView &view)
|
||||
{
|
||||
// TODO: Deal with integer view for depth-stencil images?
|
||||
push_texture(view.get_float_view(), view.get_image().get_layout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL));
|
||||
}
|
||||
|
||||
void BindlessDescriptorPool::push_texture_unorm(const ImageView &view)
|
||||
{
|
||||
push_texture(view.get_unorm_view(), view.get_image().get_layout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL));
|
||||
}
|
||||
|
||||
void BindlessDescriptorPool::push_texture_srgb(const ImageView &view)
|
||||
{
|
||||
push_texture(view.get_srgb_view(), view.get_image().get_layout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL));
|
||||
}
|
||||
|
||||
void BindlessDescriptorPool::push_texture(VkImageView view, VkImageLayout layout)
|
||||
{
|
||||
VK_ASSERT(write_count < infos.get_capacity());
|
||||
auto &image_info = infos[write_count];
|
||||
image_info = { VK_NULL_HANDLE, view, layout };
|
||||
write_count++;
|
||||
}
|
||||
|
||||
void BindlessDescriptorPool::update()
|
||||
{
|
||||
VkWriteDescriptorSet desc = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
|
||||
desc.descriptorCount = write_count;
|
||||
desc.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
||||
desc.dstSet = desc_set;
|
||||
|
||||
desc.pImageInfo = infos.data();
|
||||
desc.pBufferInfo = nullptr;
|
||||
desc.pTexelBufferView = nullptr;
|
||||
|
||||
if (write_count)
|
||||
{
|
||||
auto &table = device->get_device_table();
|
||||
table.vkUpdateDescriptorSets(device->get_device(), 1, &desc, 0, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void BindlessDescriptorPoolDeleter::operator()(BindlessDescriptorPool *pool)
|
||||
{
|
||||
pool->device->handle_pool.bindless_descriptor_pool.free(pool);
|
||||
}
|
||||
|
||||
unsigned BindlessAllocator::push(const ImageView &view)
|
||||
{
|
||||
auto ret = unsigned(views.size());
|
||||
views.push_back(&view);
|
||||
if (views.size() > VULKAN_NUM_BINDINGS_BINDLESS_VARYING)
|
||||
{
|
||||
LOGE("Exceeding maximum number of bindless resources per set (%u >= %u).\n",
|
||||
unsigned(views.size()), VULKAN_NUM_BINDINGS_BINDLESS_VARYING);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void BindlessAllocator::begin()
|
||||
{
|
||||
views.clear();
|
||||
}
|
||||
|
||||
void BindlessAllocator::reset()
|
||||
{
|
||||
descriptor_pool.reset();
|
||||
}
|
||||
|
||||
unsigned BindlessAllocator::get_next_offset() const
|
||||
{
|
||||
return unsigned(views.size());
|
||||
}
|
||||
|
||||
void BindlessAllocator::reserve_max_resources_per_pool(unsigned set_count, unsigned descriptor_count)
|
||||
{
|
||||
max_sets_per_pool = std::max(max_sets_per_pool, set_count);
|
||||
max_descriptors_per_pool = std::max(max_descriptors_per_pool, descriptor_count);
|
||||
views.reserve(max_descriptors_per_pool);
|
||||
}
|
||||
|
||||
void BindlessAllocator::set_bindless_resource_type(BindlessResourceType type)
|
||||
{
|
||||
resource_type = type;
|
||||
}
|
||||
|
||||
VkDescriptorSet BindlessAllocator::commit(Device &device)
|
||||
{
|
||||
max_sets_per_pool = std::max(1u, max_sets_per_pool);
|
||||
max_descriptors_per_pool = std::max<unsigned>(views.size(), max_descriptors_per_pool);
|
||||
max_descriptors_per_pool = std::max<unsigned>(1u, max_descriptors_per_pool);
|
||||
max_descriptors_per_pool = std::min(max_descriptors_per_pool, VULKAN_NUM_BINDINGS_BINDLESS_VARYING);
|
||||
unsigned to_allocate = std::max<unsigned>(views.size(), 1u);
|
||||
|
||||
if (!descriptor_pool)
|
||||
{
|
||||
descriptor_pool = device.create_bindless_descriptor_pool(
|
||||
resource_type, max_sets_per_pool, max_descriptors_per_pool);
|
||||
}
|
||||
|
||||
if (!descriptor_pool->allocate_descriptors(to_allocate))
|
||||
{
|
||||
descriptor_pool = device.create_bindless_descriptor_pool(
|
||||
resource_type, max_sets_per_pool, max_descriptors_per_pool);
|
||||
|
||||
if (!descriptor_pool->allocate_descriptors(to_allocate))
|
||||
{
|
||||
LOGE("Failed to allocate descriptors on a fresh descriptor pool!\n");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0, n = views.size(); i < n; i++)
|
||||
descriptor_pool->push_texture(*views[i]);
|
||||
descriptor_pool->update();
|
||||
return descriptor_pool->get_descriptor_set();
|
||||
}
|
||||
}
|
||||
192
external/parallel-rdp/parallel-rdp-standalone/vulkan/descriptor_set.hpp
vendored
Normal file
192
external/parallel-rdp/parallel-rdp-standalone/vulkan/descriptor_set.hpp
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hash.hpp"
|
||||
#include "object_pool.hpp"
|
||||
#include "temporary_hashmap.hpp"
|
||||
#include "vulkan_headers.hpp"
|
||||
#include "sampler.hpp"
|
||||
#include "limits.hpp"
|
||||
#include "dynamic_array.hpp"
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "cookie.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class Device;
|
||||
struct DescriptorSetLayout
|
||||
{
|
||||
uint32_t sampled_image_mask = 0;
|
||||
uint32_t storage_image_mask = 0;
|
||||
uint32_t uniform_buffer_mask = 0;
|
||||
uint32_t storage_buffer_mask = 0;
|
||||
uint32_t sampled_texel_buffer_mask = 0;
|
||||
uint32_t storage_texel_buffer_mask = 0;
|
||||
uint32_t input_attachment_mask = 0;
|
||||
uint32_t sampler_mask = 0;
|
||||
uint32_t separate_image_mask = 0;
|
||||
uint32_t fp_mask = 0;
|
||||
uint32_t immutable_sampler_mask = 0;
|
||||
uint8_t array_size[VULKAN_NUM_BINDINGS] = {};
|
||||
uint32_t padding = 0;
|
||||
enum { UNSIZED_ARRAY = 0xff };
|
||||
};
|
||||
|
||||
// Avoid -Wclass-memaccess warnings since we hash DescriptorSetLayout.
|
||||
|
||||
static const unsigned VULKAN_NUM_SETS_PER_POOL = 64;
|
||||
static const unsigned VULKAN_DESCRIPTOR_RING_SIZE = 16;
|
||||
|
||||
class DescriptorSetAllocator;
|
||||
class BindlessDescriptorPool;
|
||||
class ImageView;
|
||||
|
||||
struct BindlessDescriptorPoolDeleter
|
||||
{
|
||||
void operator()(BindlessDescriptorPool *pool);
|
||||
};
|
||||
|
||||
class BindlessDescriptorPool : public Util::IntrusivePtrEnabled<BindlessDescriptorPool, BindlessDescriptorPoolDeleter, HandleCounter>,
|
||||
public InternalSyncEnabled
|
||||
{
|
||||
public:
|
||||
friend struct BindlessDescriptorPoolDeleter;
|
||||
explicit BindlessDescriptorPool(Device *device, DescriptorSetAllocator *allocator, VkDescriptorPool pool,
|
||||
uint32_t total_sets, uint32_t total_descriptors);
|
||||
~BindlessDescriptorPool();
|
||||
void operator=(const BindlessDescriptorPool &) = delete;
|
||||
BindlessDescriptorPool(const BindlessDescriptorPool &) = delete;
|
||||
|
||||
void reset();
|
||||
bool allocate_descriptors(unsigned count);
|
||||
VkDescriptorSet get_descriptor_set() const;
|
||||
|
||||
void push_texture(const ImageView &view);
|
||||
void push_texture_unorm(const ImageView &view);
|
||||
void push_texture_srgb(const ImageView &view);
|
||||
void update();
|
||||
|
||||
private:
|
||||
Device *device;
|
||||
DescriptorSetAllocator *allocator;
|
||||
VkDescriptorPool desc_pool;
|
||||
VkDescriptorSet desc_set = VK_NULL_HANDLE;
|
||||
|
||||
uint32_t allocated_sets = 0;
|
||||
uint32_t total_sets = 0;
|
||||
uint32_t allocated_descriptor_count = 0;
|
||||
uint32_t total_descriptors = 0;
|
||||
|
||||
void push_texture(VkImageView view, VkImageLayout layout);
|
||||
Util::DynamicArray<VkDescriptorImageInfo> infos;
|
||||
uint32_t write_count = 0;
|
||||
};
|
||||
using BindlessDescriptorPoolHandle = Util::IntrusivePtr<BindlessDescriptorPool>;
|
||||
|
||||
enum class BindlessResourceType
|
||||
{
|
||||
Image
|
||||
};
|
||||
|
||||
class DescriptorSetAllocator : public HashedObject<DescriptorSetAllocator>
|
||||
{
|
||||
public:
|
||||
DescriptorSetAllocator(Util::Hash hash, Device *device, const DescriptorSetLayout &layout,
|
||||
const uint32_t *stages_for_bindings,
|
||||
const ImmutableSampler * const *immutable_samplers);
|
||||
~DescriptorSetAllocator();
|
||||
void operator=(const DescriptorSetAllocator &) = delete;
|
||||
DescriptorSetAllocator(const DescriptorSetAllocator &) = delete;
|
||||
|
||||
void begin_frame();
|
||||
VkDescriptorSet request_descriptor_set(unsigned thread_index, unsigned frame_context);
|
||||
|
||||
VkDescriptorSetLayout get_layout_for_pool() const
|
||||
{
|
||||
return set_layout_pool;
|
||||
}
|
||||
|
||||
VkDescriptorSetLayout get_layout_for_push() const
|
||||
{
|
||||
return set_layout_push;
|
||||
}
|
||||
|
||||
void clear();
|
||||
|
||||
bool is_bindless() const
|
||||
{
|
||||
return bindless;
|
||||
}
|
||||
|
||||
VkDescriptorPool allocate_bindless_pool(unsigned num_sets, unsigned num_descriptors);
|
||||
VkDescriptorSet allocate_bindless_set(VkDescriptorPool pool, unsigned num_descriptors);
|
||||
void reset_bindless_pool(VkDescriptorPool pool);
|
||||
|
||||
private:
|
||||
Device *device;
|
||||
const VolkDeviceTable &table;
|
||||
VkDescriptorSetLayout set_layout_pool = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout set_layout_push = VK_NULL_HANDLE;
|
||||
|
||||
struct Pool
|
||||
{
|
||||
VkDescriptorPool pool;
|
||||
VkDescriptorSet sets[VULKAN_NUM_SETS_PER_POOL];
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
class BindlessAllocator
|
||||
{
|
||||
public:
|
||||
void reserve_max_resources_per_pool(unsigned set_count, unsigned descriptor_count);
|
||||
void set_bindless_resource_type(BindlessResourceType type);
|
||||
|
||||
void begin();
|
||||
unsigned push(const ImageView &view);
|
||||
VkDescriptorSet commit(Device &device);
|
||||
|
||||
unsigned get_next_offset() const;
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
BindlessDescriptorPoolHandle descriptor_pool;
|
||||
unsigned max_sets_per_pool = 0;
|
||||
unsigned max_descriptors_per_pool = 0;
|
||||
BindlessResourceType resource_type = BindlessResourceType::Image;
|
||||
std::vector<const ImageView *> views;
|
||||
};
|
||||
}
|
||||
5247
external/parallel-rdp/parallel-rdp-standalone/vulkan/device.cpp
vendored
Normal file
5247
external/parallel-rdp/parallel-rdp-standalone/vulkan/device.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
911
external/parallel-rdp/parallel-rdp-standalone/vulkan/device.hpp
vendored
Normal file
911
external/parallel-rdp/parallel-rdp-standalone/vulkan/device.hpp
vendored
Normal file
@@ -0,0 +1,911 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "buffer.hpp"
|
||||
#include "command_buffer.hpp"
|
||||
#include "command_pool.hpp"
|
||||
#include "fence.hpp"
|
||||
#include "fence_manager.hpp"
|
||||
#include "image.hpp"
|
||||
#include "memory_allocator.hpp"
|
||||
#include "render_pass.hpp"
|
||||
#include "sampler.hpp"
|
||||
#include "semaphore.hpp"
|
||||
#include "semaphore_manager.hpp"
|
||||
#include "event_manager.hpp"
|
||||
#include "shader.hpp"
|
||||
#include "context.hpp"
|
||||
#include "query_pool.hpp"
|
||||
#include "buffer_pool.hpp"
|
||||
#include "indirect_layout.hpp"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef GRANITE_VULKAN_SYSTEM_HANDLES
|
||||
#include "shader_manager.hpp"
|
||||
#include "resource_manager.hpp"
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#ifdef GRANITE_VULKAN_FOSSILIZE
|
||||
#include "fossilize.hpp"
|
||||
#endif
|
||||
|
||||
#include "quirks.hpp"
|
||||
#include "small_vector.hpp"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
class TimelineTraceFile;
|
||||
}
|
||||
|
||||
namespace Granite
|
||||
{
|
||||
struct TaskGroup;
|
||||
}
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
enum class SwapchainRenderPass
|
||||
{
|
||||
ColorOnly,
|
||||
Depth,
|
||||
DepthStencil
|
||||
};
|
||||
|
||||
struct InitialImageBuffer
|
||||
{
|
||||
BufferHandle buffer;
|
||||
Util::SmallVector<VkBufferImageCopy, 32> blits;
|
||||
};
|
||||
|
||||
struct HandlePool
|
||||
{
|
||||
VulkanObjectPool<Buffer> buffers;
|
||||
VulkanObjectPool<Image> images;
|
||||
VulkanObjectPool<LinearHostImage> linear_images;
|
||||
VulkanObjectPool<ImageView> image_views;
|
||||
VulkanObjectPool<BufferView> buffer_views;
|
||||
VulkanObjectPool<Sampler> samplers;
|
||||
VulkanObjectPool<FenceHolder> fences;
|
||||
VulkanObjectPool<SemaphoreHolder> semaphores;
|
||||
VulkanObjectPool<EventHolder> events;
|
||||
VulkanObjectPool<QueryPoolResult> query;
|
||||
VulkanObjectPool<CommandBuffer> command_buffers;
|
||||
VulkanObjectPool<BindlessDescriptorPool> bindless_descriptor_pool;
|
||||
VulkanObjectPool<DeviceAllocationOwner> allocations;
|
||||
};
|
||||
|
||||
class DebugChannelInterface
|
||||
{
|
||||
public:
|
||||
union Word
|
||||
{
|
||||
uint32_t u32;
|
||||
int32_t s32;
|
||||
float f32;
|
||||
};
|
||||
virtual void message(const std::string &tag, uint32_t code, uint32_t x, uint32_t y, uint32_t z,
|
||||
uint32_t word_count, const Word *words) = 0;
|
||||
};
|
||||
|
||||
namespace Helper
|
||||
{
|
||||
struct WaitSemaphores
|
||||
{
|
||||
Util::SmallVector<VkSemaphoreSubmitInfo> binary_waits;
|
||||
Util::SmallVector<VkSemaphoreSubmitInfo> timeline_waits;
|
||||
};
|
||||
|
||||
class BatchComposer
|
||||
{
|
||||
public:
|
||||
enum { MaxSubmissions = 8 };
|
||||
|
||||
BatchComposer();
|
||||
void add_wait_submissions(WaitSemaphores &sem);
|
||||
void add_wait_semaphore(SemaphoreHolder &sem, VkPipelineStageFlags2 stage);
|
||||
void add_wait_semaphore(VkSemaphore sem, VkPipelineStageFlags2 stage);
|
||||
void add_signal_semaphore(VkSemaphore sem, VkPipelineStageFlags2 stage, uint64_t count);
|
||||
void add_command_buffer(VkCommandBuffer cmd);
|
||||
|
||||
void begin_batch();
|
||||
Util::SmallVector<VkSubmitInfo2, MaxSubmissions> &bake(int profiling_iteration = -1);
|
||||
|
||||
private:
|
||||
Util::SmallVector<VkSubmitInfo2, MaxSubmissions> submits;
|
||||
VkPerformanceQuerySubmitInfoKHR profiling_infos[Helper::BatchComposer::MaxSubmissions];
|
||||
|
||||
Util::SmallVector<VkSemaphoreSubmitInfo> waits[MaxSubmissions];
|
||||
Util::SmallVector<VkSemaphoreSubmitInfo> signals[MaxSubmissions];
|
||||
Util::SmallVector<VkCommandBufferSubmitInfo> cmds[MaxSubmissions];
|
||||
|
||||
unsigned submit_index = 0;
|
||||
};
|
||||
}
|
||||
|
||||
class Device
|
||||
: public Util::IntrusivePtrEnabled<Device, std::default_delete<Device>, HandleCounter>
|
||||
#ifdef GRANITE_VULKAN_FOSSILIZE
|
||||
, public Fossilize::StateCreatorInterface
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
// Device-based objects which need to poke at internal data structures when their lifetimes end.
|
||||
// Don't want to expose a lot of internal guts to make this work.
|
||||
friend class QueryPool;
|
||||
friend struct QueryPoolResultDeleter;
|
||||
friend class EventHolder;
|
||||
friend struct EventHolderDeleter;
|
||||
friend class SemaphoreHolder;
|
||||
friend struct SemaphoreHolderDeleter;
|
||||
friend class FenceHolder;
|
||||
friend struct FenceHolderDeleter;
|
||||
friend class Sampler;
|
||||
friend struct SamplerDeleter;
|
||||
friend class ImmutableSampler;
|
||||
friend class ImmutableYcbcrConversion;
|
||||
friend class Buffer;
|
||||
friend struct BufferDeleter;
|
||||
friend class BufferView;
|
||||
friend struct BufferViewDeleter;
|
||||
friend class ImageView;
|
||||
friend struct ImageViewDeleter;
|
||||
friend class Image;
|
||||
friend struct ImageDeleter;
|
||||
friend struct LinearHostImageDeleter;
|
||||
friend class CommandBuffer;
|
||||
friend struct CommandBufferDeleter;
|
||||
friend class BindlessDescriptorPool;
|
||||
friend struct BindlessDescriptorPoolDeleter;
|
||||
friend class Program;
|
||||
friend class WSI;
|
||||
friend class Cookie;
|
||||
friend class Framebuffer;
|
||||
friend class PipelineLayout;
|
||||
friend class FramebufferAllocator;
|
||||
friend class RenderPass;
|
||||
friend class Texture;
|
||||
friend class DescriptorSetAllocator;
|
||||
friend class Shader;
|
||||
friend class ImageResourceHolder;
|
||||
friend class DeviceAllocationOwner;
|
||||
friend struct DeviceAllocationDeleter;
|
||||
|
||||
Device();
|
||||
~Device();
|
||||
|
||||
// No move-copy.
|
||||
void operator=(Device &&) = delete;
|
||||
Device(Device &&) = delete;
|
||||
|
||||
// Only called by main thread, during setup phase.
|
||||
void set_context(const Context &context);
|
||||
|
||||
// This is asynchronous in nature. See query_initialization_progress().
|
||||
// Kicks off Fossilize and shader manager caching.
|
||||
void begin_shader_caches();
|
||||
// For debug or trivial applications, blocks until all shader cache work is done.
|
||||
void wait_shader_caches();
|
||||
|
||||
void init_swapchain(const std::vector<VkImage> &swapchain_images, unsigned width, unsigned height, VkFormat format,
|
||||
VkSurfaceTransformFlagBitsKHR transform, VkImageUsageFlags usage);
|
||||
void set_swapchain_queue_family_support(uint32_t queue_family_support);
|
||||
bool can_touch_swapchain_in_command_buffer(CommandBuffer::Type type) const;
|
||||
void init_external_swapchain(const std::vector<ImageHandle> &swapchain_images);
|
||||
void init_frame_contexts(unsigned count);
|
||||
const VolkDeviceTable &get_device_table() const;
|
||||
|
||||
// Profiling
|
||||
bool init_performance_counters(CommandBuffer::Type type, const std::vector<std::string> &names);
|
||||
bool acquire_profiling();
|
||||
void release_profiling();
|
||||
void query_available_performance_counters(CommandBuffer::Type type,
|
||||
uint32_t *count,
|
||||
const VkPerformanceCounterKHR **counters,
|
||||
const VkPerformanceCounterDescriptionKHR **desc);
|
||||
|
||||
ImageView &get_swapchain_view();
|
||||
ImageView &get_swapchain_view(unsigned index);
|
||||
unsigned get_num_swapchain_images() const;
|
||||
unsigned get_num_frame_contexts() const;
|
||||
unsigned get_swapchain_index() const;
|
||||
unsigned get_current_frame_context() const;
|
||||
|
||||
size_t get_pipeline_cache_size();
|
||||
bool get_pipeline_cache_data(uint8_t *data, size_t size);
|
||||
bool init_pipeline_cache(const uint8_t *data, size_t size);
|
||||
|
||||
// Frame-pushing interface.
|
||||
void next_frame_context();
|
||||
|
||||
// Normally, the main thread ensures forward progress of the frame context
|
||||
// so that async tasks don't have to care about it,
|
||||
// but in the case where async threads are continuously pumping Vulkan work
|
||||
// in the background, they need to reclaim memory if WSI goes to sleep for a long period of time.
|
||||
void next_frame_context_in_async_thread();
|
||||
void set_enable_async_thread_frame_context(bool enable);
|
||||
|
||||
void wait_idle();
|
||||
void end_frame_context();
|
||||
|
||||
// RenderDoc integration API for app-guided captures.
|
||||
static bool init_renderdoc_capture();
|
||||
// Calls next_frame_context() and begins a renderdoc capture.
|
||||
void begin_renderdoc_capture();
|
||||
// Calls next_frame_context() and ends the renderdoc capture.
|
||||
void end_renderdoc_capture();
|
||||
|
||||
// Set names for objects for debuggers and profilers.
|
||||
void set_name(const Buffer &buffer, const char *name);
|
||||
void set_name(const Image &image, const char *name);
|
||||
void set_name(const CommandBuffer &cmd, const char *name);
|
||||
// Generic version.
|
||||
void set_name(uint64_t object, VkObjectType type, const char *name);
|
||||
|
||||
// Submission interface, may be called from any thread at any time.
|
||||
void flush_frame();
|
||||
CommandBufferHandle request_command_buffer(CommandBuffer::Type type = CommandBuffer::Type::Generic);
|
||||
CommandBufferHandle request_command_buffer_for_thread(unsigned thread_index, CommandBuffer::Type type = CommandBuffer::Type::Generic);
|
||||
|
||||
CommandBufferHandle request_profiled_command_buffer(CommandBuffer::Type type = CommandBuffer::Type::Generic);
|
||||
CommandBufferHandle request_profiled_command_buffer_for_thread(unsigned thread_index, CommandBuffer::Type type = CommandBuffer::Type::Generic);
|
||||
|
||||
void submit(CommandBufferHandle &cmd, Fence *fence = nullptr,
|
||||
unsigned semaphore_count = 0, Semaphore *semaphore = nullptr);
|
||||
|
||||
void submit_empty(CommandBuffer::Type type,
|
||||
Fence *fence = nullptr,
|
||||
SemaphoreHolder *semaphore = nullptr);
|
||||
// Mark that there have been work submitted in this frame context outside our control
|
||||
// that accesses resources Vulkan::Device owns.
|
||||
void submit_external(CommandBuffer::Type type);
|
||||
void submit_discard(CommandBufferHandle &cmd);
|
||||
QueueIndices get_physical_queue_type(CommandBuffer::Type queue_type) const;
|
||||
void register_time_interval(std::string tid, QueryPoolHandle start_ts, QueryPoolHandle end_ts,
|
||||
const std::string &tag);
|
||||
|
||||
// Request shaders and programs. These objects are owned by the Device.
|
||||
Shader *request_shader(const uint32_t *code, size_t size, const ResourceLayout *layout = nullptr);
|
||||
Shader *request_shader_by_hash(Util::Hash hash);
|
||||
Program *request_program(const uint32_t *task_data, size_t task_size,
|
||||
const uint32_t *mesh_data, size_t mesh_size,
|
||||
const uint32_t *fragment_data, size_t fragment_size,
|
||||
const ResourceLayout *task_layout = nullptr,
|
||||
const ResourceLayout *mesh_layout = nullptr,
|
||||
const ResourceLayout *fragment_layout = nullptr);
|
||||
Program *request_program(const uint32_t *vertex_data, size_t vertex_size,
|
||||
const uint32_t *fragment_data, size_t fragment_size,
|
||||
const ResourceLayout *vertex_layout = nullptr,
|
||||
const ResourceLayout *fragment_layout = nullptr);
|
||||
Program *request_program(const uint32_t *compute_data, size_t compute_size,
|
||||
const ResourceLayout *layout = nullptr);
|
||||
Program *request_program(Shader *task, Shader *mesh, Shader *fragment, const ImmutableSamplerBank *sampler_bank = nullptr);
|
||||
Program *request_program(Shader *vertex, Shader *fragment, const ImmutableSamplerBank *sampler_bank = nullptr);
|
||||
Program *request_program(Shader *compute, const ImmutableSamplerBank *sampler_bank = nullptr);
|
||||
const IndirectLayout *request_indirect_layout(const IndirectLayoutToken *tokens,
|
||||
uint32_t num_tokens, uint32_t stride);
|
||||
|
||||
const ImmutableYcbcrConversion *request_immutable_ycbcr_conversion(const VkSamplerYcbcrConversionCreateInfo &info);
|
||||
const ImmutableSampler *request_immutable_sampler(const SamplerCreateInfo &info, const ImmutableYcbcrConversion *ycbcr);
|
||||
|
||||
// Map and unmap buffer objects.
|
||||
void *map_host_buffer(const Buffer &buffer, MemoryAccessFlags access);
|
||||
void unmap_host_buffer(const Buffer &buffer, MemoryAccessFlags access);
|
||||
void *map_host_buffer(const Buffer &buffer, MemoryAccessFlags access, VkDeviceSize offset, VkDeviceSize length);
|
||||
void unmap_host_buffer(const Buffer &buffer, MemoryAccessFlags access, VkDeviceSize offset, VkDeviceSize length);
|
||||
|
||||
void *map_linear_host_image(const LinearHostImage &image, MemoryAccessFlags access);
|
||||
void unmap_linear_host_image_and_sync(const LinearHostImage &image, MemoryAccessFlags access);
|
||||
|
||||
// Create buffers and images.
|
||||
BufferHandle create_buffer(const BufferCreateInfo &info, const void *initial = nullptr);
|
||||
BufferHandle create_imported_host_buffer(const BufferCreateInfo &info, VkExternalMemoryHandleTypeFlagBits type, void *host_buffer);
|
||||
ImageHandle create_image(const ImageCreateInfo &info, const ImageInitialData *initial = nullptr);
|
||||
ImageHandle create_image_from_staging_buffer(const ImageCreateInfo &info, const InitialImageBuffer *buffer);
|
||||
LinearHostImageHandle create_linear_host_image(const LinearHostImageCreateInfo &info);
|
||||
// Does not create any default image views. Only wraps the VkImage
|
||||
// as a non-owned handle for purposes of API interop.
|
||||
ImageHandle wrap_image(const ImageCreateInfo &info, VkImage img);
|
||||
DeviceAllocationOwnerHandle take_device_allocation_ownership(Image &image);
|
||||
DeviceAllocationOwnerHandle allocate_memory(const MemoryAllocateInfo &info);
|
||||
|
||||
// Create staging buffers for images.
|
||||
InitialImageBuffer create_image_staging_buffer(const ImageCreateInfo &info, const ImageInitialData *initial);
|
||||
InitialImageBuffer create_image_staging_buffer(const TextureFormatLayout &layout);
|
||||
|
||||
// Create image view, buffer views and samplers.
|
||||
ImageViewHandle create_image_view(const ImageViewCreateInfo &view_info);
|
||||
BufferViewHandle create_buffer_view(const BufferViewCreateInfo &view_info);
|
||||
SamplerHandle create_sampler(const SamplerCreateInfo &info);
|
||||
|
||||
BindlessDescriptorPoolHandle create_bindless_descriptor_pool(BindlessResourceType type,
|
||||
unsigned num_sets, unsigned num_descriptors);
|
||||
|
||||
// Render pass helpers.
|
||||
bool image_format_is_supported(VkFormat format, VkFormatFeatureFlags2KHR required, VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL) const;
|
||||
void get_format_properties(VkFormat format, VkFormatProperties3KHR *properties) const;
|
||||
bool get_image_format_properties(VkFormat format, VkImageType type, VkImageTiling tiling,
|
||||
VkImageUsageFlags usage, VkImageCreateFlags flags,
|
||||
const void *pNext,
|
||||
VkImageFormatProperties2 *properties2) const;
|
||||
|
||||
VkFormat get_default_depth_stencil_format() const;
|
||||
VkFormat get_default_depth_format() const;
|
||||
ImageHandle get_transient_attachment(unsigned width, unsigned height, VkFormat format,
|
||||
unsigned index = 0, unsigned samples = 1, unsigned layers = 1);
|
||||
RenderPassInfo get_swapchain_render_pass(SwapchainRenderPass style);
|
||||
|
||||
// Semaphore API:
|
||||
// Semaphores in Granite are abstracted to support both binary and timeline semaphores
|
||||
// internally.
|
||||
// In practice this means that semaphores behave like single-use binary semaphores,
|
||||
// with one signal and one wait.
|
||||
// A single semaphore handle is not reused for multiple submissions, and they must be recycled through
|
||||
// the device. The intended use is device.submit(&sem), device.add_wait_semaphore(sem); dispose(sem);
|
||||
// For timeline semaphores, the semaphore is just a proxy object which
|
||||
// holds the internally owned VkSemaphore + timeline value and is otherwise lightweight.
|
||||
//
|
||||
// However, there are various use cases where we explicitly need semaphore objects:
|
||||
// - Interoperate with other code that only accepts VkSemaphore.
|
||||
// - Interoperate with external objects. We need to know whether to use binary or timeline.
|
||||
// For timelines, we need to know which handle type to use (OPAQUE or ID3D12Fence).
|
||||
// Binary external semaphore is always opaque with TEMPORARY semantics.
|
||||
|
||||
void add_wait_semaphore(CommandBuffer::Type type, Semaphore semaphore, VkPipelineStageFlags2 stages, bool flush);
|
||||
|
||||
// If transfer_ownership is set, Semaphore owns the VkSemaphore. Otherwise, application must
|
||||
// free the semaphore when GPU usage of it is complete.
|
||||
Semaphore request_semaphore(VkSemaphoreTypeKHR type, VkSemaphore handle = VK_NULL_HANDLE, bool transfer_ownership = false);
|
||||
|
||||
// Requests a binary or timeline semaphore that can be used to import/export.
|
||||
// These semaphores cannot be used directly by add_wait_semaphore() and submit_empty().
|
||||
// See request_timeline_semaphore_as_binary() for how to use timelines.
|
||||
Semaphore request_semaphore_external(VkSemaphoreTypeKHR type,
|
||||
VkExternalSemaphoreHandleTypeFlagBits handle_type);
|
||||
|
||||
// The created semaphore does not hold ownership of the VkSemaphore object.
|
||||
// This is used when we want to wait on or signal an external timeline semaphore at a specific timeline value.
|
||||
// We must collapse the timeline to a "binary" semaphore before we can call submit_empty or add_wait_semaphore().
|
||||
Semaphore request_timeline_semaphore_as_binary(const SemaphoreHolder &holder, uint64_t value);
|
||||
|
||||
// A proxy semaphore which lets us grab a semaphore handle before we signal it.
|
||||
// Move assignment can be used to move a payload.
|
||||
// Mostly useful to deal better with render graph implementation.
|
||||
// For time being however, we'll support moving the payload over to the proxy object.
|
||||
Semaphore request_proxy_semaphore();
|
||||
|
||||
// For compat with existing code that uses this entry point.
|
||||
inline Semaphore request_legacy_semaphore() { return request_semaphore(VK_SEMAPHORE_TYPE_BINARY_KHR); }
|
||||
|
||||
inline VkDevice get_device() const
|
||||
{
|
||||
return device;
|
||||
}
|
||||
|
||||
inline VkPhysicalDevice get_physical_device() const
|
||||
{
|
||||
return gpu;
|
||||
}
|
||||
|
||||
inline VkInstance get_instance() const
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
inline const VkPhysicalDeviceMemoryProperties &get_memory_properties() const
|
||||
{
|
||||
return mem_props;
|
||||
}
|
||||
|
||||
inline const VkPhysicalDeviceProperties &get_gpu_properties() const
|
||||
{
|
||||
return gpu_props;
|
||||
}
|
||||
|
||||
void get_memory_budget(HeapBudget *budget);
|
||||
|
||||
const Sampler &get_stock_sampler(StockSampler sampler) const;
|
||||
|
||||
#ifdef GRANITE_VULKAN_SYSTEM_HANDLES
|
||||
// To obtain ShaderManager, ShaderModules must be observed to be complete
|
||||
// in query_initialization_progress().
|
||||
ShaderManager &get_shader_manager();
|
||||
ResourceManager &get_resource_manager();
|
||||
#endif
|
||||
|
||||
// Useful for loading screens or otherwise figuring out
|
||||
// when we can start rendering in a stable state.
|
||||
enum class InitializationStage
|
||||
{
|
||||
CacheMaintenance,
|
||||
// When this is done, shader modules and the shader manager have been populated.
|
||||
// At this stage it is safe to use shaders in a configuration where we
|
||||
// don't have SPIRV-Cross and/or shaderc to do on the fly compilation.
|
||||
// For shipping configurations. We can still compile pipelines, but it may stutter.
|
||||
ShaderModules,
|
||||
// When this is done, pipelines should never stutter if Fossilize knows about the pipeline.
|
||||
Pipelines
|
||||
};
|
||||
|
||||
// 0 -> not started
|
||||
// [1, 99] rough percentage of completion
|
||||
// >= 100 done
|
||||
unsigned query_initialization_progress(InitializationStage status) const;
|
||||
|
||||
// For some platforms, the device and queue might be shared, possibly across threads, so need some mechanism to
|
||||
// lock the global device and queue.
|
||||
void set_queue_lock(std::function<void ()> lock_callback,
|
||||
std::function<void ()> unlock_callback);
|
||||
|
||||
// Alternative form, when we have to provide lock callbacks to external APIs.
|
||||
void external_queue_lock();
|
||||
void external_queue_unlock();
|
||||
|
||||
const ImplementationWorkarounds &get_workarounds() const
|
||||
{
|
||||
return workarounds;
|
||||
}
|
||||
|
||||
const DeviceFeatures &get_device_features() const
|
||||
{
|
||||
return ext;
|
||||
}
|
||||
|
||||
bool consumes_debug_markers() const
|
||||
{
|
||||
return debug_marker_sensitive;
|
||||
}
|
||||
|
||||
bool swapchain_touched() const;
|
||||
|
||||
double convert_device_timestamp_delta(uint64_t start_ticks, uint64_t end_ticks) const;
|
||||
// Writes a timestamp on host side, which is calibrated to the GPU timebase.
|
||||
QueryPoolHandle write_calibrated_timestamp();
|
||||
|
||||
// A split version of VkEvent handling which lets us record a wait command before signal is recorded.
|
||||
PipelineEvent begin_signal_event();
|
||||
|
||||
const Context::SystemHandles &get_system_handles() const
|
||||
{
|
||||
return system_handles;
|
||||
}
|
||||
|
||||
void configure_default_geometry_samplers(float max_aniso, float lod_bias);
|
||||
|
||||
bool supports_subgroup_size_log2(bool subgroup_full_group,
|
||||
uint8_t subgroup_minimum_size_log2,
|
||||
uint8_t subgroup_maximum_size_log2,
|
||||
VkShaderStageFlagBits stage = VK_SHADER_STAGE_COMPUTE_BIT) const;
|
||||
|
||||
const QueueInfo &get_queue_info() const;
|
||||
|
||||
void timestamp_log_reset();
|
||||
void timestamp_log(const TimestampIntervalReportCallback &cb) const;
|
||||
|
||||
private:
|
||||
VkInstance instance = VK_NULL_HANDLE;
|
||||
VkPhysicalDevice gpu = VK_NULL_HANDLE;
|
||||
VkDevice device = VK_NULL_HANDLE;
|
||||
const VolkDeviceTable *table = nullptr;
|
||||
const Context *ctx = nullptr;
|
||||
QueueInfo queue_info;
|
||||
unsigned num_thread_indices = 1;
|
||||
|
||||
std::atomic_uint64_t cookie;
|
||||
|
||||
uint64_t allocate_cookie();
|
||||
void bake_program(Program &program, const ImmutableSamplerBank *sampler_bank);
|
||||
void merge_combined_resource_layout(CombinedResourceLayout &layout, const Program &program);
|
||||
|
||||
void request_vertex_block(BufferBlock &block, VkDeviceSize size);
|
||||
void request_index_block(BufferBlock &block, VkDeviceSize size);
|
||||
void request_uniform_block(BufferBlock &block, VkDeviceSize size);
|
||||
void request_staging_block(BufferBlock &block, VkDeviceSize size);
|
||||
|
||||
QueryPoolHandle write_timestamp(VkCommandBuffer cmd, VkPipelineStageFlags2 stage);
|
||||
|
||||
void set_acquire_semaphore(unsigned index, Semaphore acquire);
|
||||
Semaphore consume_release_semaphore();
|
||||
VkQueue get_current_present_queue() const;
|
||||
CommandBuffer::Type get_current_present_queue_type() const;
|
||||
|
||||
const PipelineLayout *request_pipeline_layout(const CombinedResourceLayout &layout,
|
||||
const ImmutableSamplerBank *immutable_samplers);
|
||||
DescriptorSetAllocator *request_descriptor_set_allocator(const DescriptorSetLayout &layout,
|
||||
const uint32_t *stages_for_sets,
|
||||
const ImmutableSampler * const *immutable_samplers);
|
||||
const Framebuffer &request_framebuffer(const RenderPassInfo &info);
|
||||
const RenderPass &request_render_pass(const RenderPassInfo &info, bool compatible);
|
||||
|
||||
VkPhysicalDeviceMemoryProperties mem_props;
|
||||
VkPhysicalDeviceProperties gpu_props;
|
||||
|
||||
DeviceFeatures ext;
|
||||
bool debug_marker_sensitive = false;
|
||||
void init_stock_samplers();
|
||||
void init_stock_sampler(StockSampler sampler, float max_aniso, float lod_bias);
|
||||
void init_timeline_semaphores();
|
||||
void deinit_timeline_semaphores();
|
||||
|
||||
uint64_t update_wrapped_device_timestamp(uint64_t ts);
|
||||
int64_t convert_timestamp_to_absolute_nsec(const QueryPoolResult &handle);
|
||||
Context::SystemHandles system_handles;
|
||||
|
||||
QueryPoolHandle write_timestamp_nolock(VkCommandBuffer cmd, VkPipelineStageFlags2 stage);
|
||||
QueryPoolHandle write_calibrated_timestamp_nolock();
|
||||
void register_time_interval_nolock(std::string tid, QueryPoolHandle start_ts, QueryPoolHandle end_ts,
|
||||
const std::string &tag);
|
||||
|
||||
// Make sure this is deleted last.
|
||||
HandlePool handle_pool;
|
||||
|
||||
// Calibrated timestamps.
|
||||
void init_calibrated_timestamps();
|
||||
void recalibrate_timestamps_fallback();
|
||||
void recalibrate_timestamps();
|
||||
bool resample_calibrated_timestamps();
|
||||
VkTimeDomainEXT calibrated_time_domain = VK_TIME_DOMAIN_DEVICE_EXT;
|
||||
int64_t calibrated_timestamp_device = 0;
|
||||
int64_t calibrated_timestamp_host = 0;
|
||||
int64_t calibrated_timestamp_device_accum = 0;
|
||||
unsigned timestamp_calibration_counter = 0;
|
||||
Vulkan::QueryPoolHandle frame_context_begin_ts;
|
||||
|
||||
struct Managers
|
||||
{
|
||||
DeviceAllocator memory;
|
||||
FenceManager fence;
|
||||
SemaphoreManager semaphore;
|
||||
EventManager event;
|
||||
BufferPool vbo, ibo, ubo, staging;
|
||||
TimestampIntervalManager timestamps;
|
||||
};
|
||||
Managers managers;
|
||||
|
||||
struct
|
||||
{
|
||||
std::mutex memory_lock;
|
||||
std::mutex lock;
|
||||
std::condition_variable cond;
|
||||
Util::RWSpinLock read_only_cache;
|
||||
unsigned counter = 0;
|
||||
bool async_frame_context = false;
|
||||
} lock;
|
||||
|
||||
struct PerFrame
|
||||
{
|
||||
PerFrame(Device *device, unsigned index);
|
||||
~PerFrame();
|
||||
void operator=(const PerFrame &) = delete;
|
||||
PerFrame(const PerFrame &) = delete;
|
||||
|
||||
void begin();
|
||||
void trim_command_pools();
|
||||
|
||||
Device &device;
|
||||
unsigned frame_index;
|
||||
const VolkDeviceTable &table;
|
||||
Managers &managers;
|
||||
|
||||
std::vector<CommandPool> cmd_pools[QUEUE_INDEX_COUNT];
|
||||
VkSemaphore timeline_semaphores[QUEUE_INDEX_COUNT] = {};
|
||||
uint64_t timeline_fences[QUEUE_INDEX_COUNT] = {};
|
||||
|
||||
QueryPool query_pool;
|
||||
|
||||
std::vector<BufferBlock> vbo_blocks;
|
||||
std::vector<BufferBlock> ibo_blocks;
|
||||
std::vector<BufferBlock> ubo_blocks;
|
||||
std::vector<BufferBlock> staging_blocks;
|
||||
|
||||
std::vector<VkFence> wait_and_recycle_fences;
|
||||
|
||||
std::vector<DeviceAllocation> allocations;
|
||||
std::vector<VkFramebuffer> destroyed_framebuffers;
|
||||
std::vector<VkSampler> destroyed_samplers;
|
||||
std::vector<VkImageView> destroyed_image_views;
|
||||
std::vector<VkBufferView> destroyed_buffer_views;
|
||||
std::vector<VkImage> destroyed_images;
|
||||
std::vector<VkBuffer> destroyed_buffers;
|
||||
std::vector<VkDescriptorPool> destroyed_descriptor_pools;
|
||||
Util::SmallVector<CommandBufferHandle> submissions[QUEUE_INDEX_COUNT];
|
||||
std::vector<VkSemaphore> recycled_semaphores;
|
||||
std::vector<VkEvent> recycled_events;
|
||||
std::vector<VkSemaphore> destroyed_semaphores;
|
||||
std::vector<VkSemaphore> consumed_semaphores;
|
||||
|
||||
struct DebugChannel
|
||||
{
|
||||
DebugChannelInterface *iface;
|
||||
std::string tag;
|
||||
BufferHandle buffer;
|
||||
};
|
||||
std::vector<DebugChannel> debug_channels;
|
||||
|
||||
struct TimestampIntervalHandles
|
||||
{
|
||||
std::string tid;
|
||||
QueryPoolHandle start_ts;
|
||||
QueryPoolHandle end_ts;
|
||||
TimestampInterval *timestamp_tag;
|
||||
};
|
||||
std::vector<TimestampIntervalHandles> timestamp_intervals;
|
||||
|
||||
bool in_destructor = false;
|
||||
};
|
||||
// The per frame structure must be destroyed after
|
||||
// the hashmap data structures below, so it must be declared before.
|
||||
std::vector<std::unique_ptr<PerFrame>> per_frame;
|
||||
|
||||
struct
|
||||
{
|
||||
Semaphore acquire;
|
||||
Semaphore release;
|
||||
std::vector<ImageHandle> swapchain;
|
||||
VkQueue present_queue = VK_NULL_HANDLE;
|
||||
Vulkan::CommandBuffer::Type present_queue_type = {};
|
||||
uint32_t queue_family_support_mask = 0;
|
||||
unsigned index = 0;
|
||||
bool consumed = false;
|
||||
} wsi;
|
||||
bool can_touch_swapchain_in_command_buffer(QueueIndices physical_type) const;
|
||||
|
||||
struct QueueData
|
||||
{
|
||||
Util::SmallVector<Semaphore> wait_semaphores;
|
||||
Util::SmallVector<VkPipelineStageFlags2> wait_stages;
|
||||
bool need_fence = false;
|
||||
|
||||
VkSemaphore timeline_semaphore = VK_NULL_HANDLE;
|
||||
uint64_t current_timeline = 0;
|
||||
PerformanceQueryPool performance_query_pool;
|
||||
} queue_data[QUEUE_INDEX_COUNT];
|
||||
|
||||
struct InternalFence
|
||||
{
|
||||
VkFence fence;
|
||||
VkSemaphore timeline;
|
||||
uint64_t value;
|
||||
};
|
||||
|
||||
void submit_queue(QueueIndices physical_type, InternalFence *fence,
|
||||
SemaphoreHolder *external_semaphore = nullptr,
|
||||
unsigned semaphore_count = 0,
|
||||
Semaphore *semaphore = nullptr,
|
||||
int profiled_iteration = -1);
|
||||
|
||||
PerFrame &frame()
|
||||
{
|
||||
VK_ASSERT(frame_context_index < per_frame.size());
|
||||
VK_ASSERT(per_frame[frame_context_index]);
|
||||
return *per_frame[frame_context_index];
|
||||
}
|
||||
|
||||
const PerFrame &frame() const
|
||||
{
|
||||
VK_ASSERT(frame_context_index < per_frame.size());
|
||||
VK_ASSERT(per_frame[frame_context_index]);
|
||||
return *per_frame[frame_context_index];
|
||||
}
|
||||
|
||||
unsigned frame_context_index = 0;
|
||||
|
||||
uint32_t find_memory_type(BufferDomain domain, uint32_t mask) const;
|
||||
uint32_t find_memory_type(ImageDomain domain, uint32_t mask) const;
|
||||
uint32_t find_memory_type(uint32_t required, uint32_t mask) const;
|
||||
bool memory_type_is_device_optimal(uint32_t type) const;
|
||||
bool memory_type_is_host_visible(uint32_t type) const;
|
||||
|
||||
const ImmutableSampler *samplers[static_cast<unsigned>(StockSampler::Count)] = {};
|
||||
|
||||
VulkanCache<PipelineLayout> pipeline_layouts;
|
||||
VulkanCache<DescriptorSetAllocator> descriptor_set_allocators;
|
||||
VulkanCache<RenderPass> render_passes;
|
||||
VulkanCache<Shader> shaders;
|
||||
VulkanCache<Program> programs;
|
||||
VulkanCache<ImmutableSampler> immutable_samplers;
|
||||
VulkanCache<ImmutableYcbcrConversion> immutable_ycbcr_conversions;
|
||||
VulkanCache<IndirectLayout> indirect_layouts;
|
||||
|
||||
FramebufferAllocator framebuffer_allocator;
|
||||
TransientAttachmentAllocator transient_allocator;
|
||||
VkPipelineCache pipeline_cache = VK_NULL_HANDLE;
|
||||
|
||||
void init_pipeline_cache();
|
||||
void flush_pipeline_cache();
|
||||
|
||||
PerformanceQueryPool &get_performance_query_pool(QueueIndices physical_type);
|
||||
void clear_wait_semaphores();
|
||||
void submit_staging(CommandBufferHandle &cmd, bool flush);
|
||||
PipelineEvent request_pipeline_event();
|
||||
|
||||
std::function<void ()> queue_lock_callback;
|
||||
std::function<void ()> queue_unlock_callback;
|
||||
void flush_frame(QueueIndices physical_type);
|
||||
void submit_empty_inner(QueueIndices type, InternalFence *fence,
|
||||
SemaphoreHolder *external_semaphore,
|
||||
unsigned semaphore_count,
|
||||
Semaphore *semaphore);
|
||||
|
||||
void collect_wait_semaphores(QueueData &data, Helper::WaitSemaphores &semaphores);
|
||||
void emit_queue_signals(Helper::BatchComposer &composer,
|
||||
SemaphoreHolder *external_semaphore,
|
||||
VkSemaphore sem, uint64_t timeline, InternalFence *fence,
|
||||
unsigned semaphore_count, Semaphore *semaphores);
|
||||
VkResult submit_batches(Helper::BatchComposer &composer, VkQueue queue, VkFence fence,
|
||||
int profiling_iteration = -1);
|
||||
VkResult queue_submit(VkQueue queue, uint32_t count, const VkSubmitInfo2 *submits, VkFence fence);
|
||||
|
||||
void destroy_buffer(VkBuffer buffer);
|
||||
void destroy_image(VkImage image);
|
||||
void destroy_image_view(VkImageView view);
|
||||
void destroy_buffer_view(VkBufferView view);
|
||||
void destroy_sampler(VkSampler sampler);
|
||||
void destroy_framebuffer(VkFramebuffer framebuffer);
|
||||
void destroy_semaphore(VkSemaphore semaphore);
|
||||
void consume_semaphore(VkSemaphore semaphore);
|
||||
void recycle_semaphore(VkSemaphore semaphore);
|
||||
void destroy_event(VkEvent event);
|
||||
void free_memory(const DeviceAllocation &alloc);
|
||||
void reset_fence(VkFence fence, bool observed_wait);
|
||||
void destroy_descriptor_pool(VkDescriptorPool desc_pool);
|
||||
|
||||
void destroy_buffer_nolock(VkBuffer buffer);
|
||||
void destroy_image_nolock(VkImage image);
|
||||
void destroy_image_view_nolock(VkImageView view);
|
||||
void destroy_buffer_view_nolock(VkBufferView view);
|
||||
void destroy_sampler_nolock(VkSampler sampler);
|
||||
void destroy_framebuffer_nolock(VkFramebuffer framebuffer);
|
||||
void destroy_semaphore_nolock(VkSemaphore semaphore);
|
||||
void consume_semaphore_nolock(VkSemaphore semaphore);
|
||||
void recycle_semaphore_nolock(VkSemaphore semaphore);
|
||||
void destroy_event_nolock(VkEvent event);
|
||||
void free_memory_nolock(const DeviceAllocation &alloc);
|
||||
void destroy_descriptor_pool_nolock(VkDescriptorPool desc_pool);
|
||||
void reset_fence_nolock(VkFence fence, bool observed_wait);
|
||||
|
||||
void flush_frame_nolock();
|
||||
CommandBufferHandle request_command_buffer_nolock(unsigned thread_index, CommandBuffer::Type type, bool profiled);
|
||||
void submit_discard_nolock(CommandBufferHandle &cmd);
|
||||
void submit_nolock(CommandBufferHandle cmd, Fence *fence,
|
||||
unsigned semaphore_count, Semaphore *semaphore);
|
||||
void submit_empty_nolock(QueueIndices physical_type, Fence *fence,
|
||||
SemaphoreHolder *semaphore, int profiling_iteration);
|
||||
void add_wait_semaphore_nolock(QueueIndices type, Semaphore semaphore,
|
||||
VkPipelineStageFlags2 stages, bool flush);
|
||||
|
||||
void request_vertex_block_nolock(BufferBlock &block, VkDeviceSize size);
|
||||
void request_index_block_nolock(BufferBlock &block, VkDeviceSize size);
|
||||
void request_uniform_block_nolock(BufferBlock &block, VkDeviceSize size);
|
||||
void request_staging_block_nolock(BufferBlock &block, VkDeviceSize size);
|
||||
|
||||
CommandBufferHandle request_secondary_command_buffer_for_thread(unsigned thread_index,
|
||||
const Framebuffer *framebuffer,
|
||||
unsigned subpass,
|
||||
CommandBuffer::Type type = CommandBuffer::Type::Generic);
|
||||
void add_frame_counter_nolock();
|
||||
void decrement_frame_counter_nolock();
|
||||
void submit_secondary(CommandBuffer &primary, CommandBuffer &secondary);
|
||||
void wait_idle_nolock();
|
||||
void end_frame_nolock();
|
||||
|
||||
void add_debug_channel_buffer(DebugChannelInterface *iface, std::string tag, BufferHandle buffer);
|
||||
void parse_debug_channel(const PerFrame::DebugChannel &channel);
|
||||
|
||||
Fence request_legacy_fence();
|
||||
|
||||
#ifdef GRANITE_VULKAN_SYSTEM_HANDLES
|
||||
ShaderManager shader_manager;
|
||||
ResourceManager resource_manager;
|
||||
void init_shader_manager_cache();
|
||||
void flush_shader_manager_cache();
|
||||
#endif
|
||||
|
||||
#ifdef GRANITE_VULKAN_FOSSILIZE
|
||||
bool enqueue_create_sampler(Fossilize::Hash hash, const VkSamplerCreateInfo *create_info, VkSampler *sampler) override;
|
||||
bool enqueue_create_descriptor_set_layout(Fossilize::Hash hash, const VkDescriptorSetLayoutCreateInfo *create_info, VkDescriptorSetLayout *layout) override;
|
||||
bool enqueue_create_pipeline_layout(Fossilize::Hash hash, const VkPipelineLayoutCreateInfo *create_info, VkPipelineLayout *layout) override;
|
||||
bool enqueue_create_shader_module(Fossilize::Hash hash, const VkShaderModuleCreateInfo *create_info, VkShaderModule *module) override;
|
||||
bool enqueue_create_render_pass(Fossilize::Hash hash, const VkRenderPassCreateInfo *create_info, VkRenderPass *render_pass) override;
|
||||
bool enqueue_create_render_pass2(Fossilize::Hash hash, const VkRenderPassCreateInfo2 *create_info, VkRenderPass *render_pass) override;
|
||||
bool enqueue_create_compute_pipeline(Fossilize::Hash hash, const VkComputePipelineCreateInfo *create_info, VkPipeline *pipeline) override;
|
||||
bool enqueue_create_graphics_pipeline(Fossilize::Hash hash, const VkGraphicsPipelineCreateInfo *create_info, VkPipeline *pipeline) override;
|
||||
bool enqueue_create_raytracing_pipeline(Fossilize::Hash hash, const VkRayTracingPipelineCreateInfoKHR *create_info, VkPipeline *pipeline) override;
|
||||
bool fossilize_replay_graphics_pipeline(Fossilize::Hash hash, VkGraphicsPipelineCreateInfo &info);
|
||||
bool fossilize_replay_compute_pipeline(Fossilize::Hash hash, VkComputePipelineCreateInfo &info);
|
||||
|
||||
void replay_tag_simple(Fossilize::ResourceTag tag);
|
||||
|
||||
void register_graphics_pipeline(Fossilize::Hash hash, const VkGraphicsPipelineCreateInfo &info);
|
||||
void register_compute_pipeline(Fossilize::Hash hash, const VkComputePipelineCreateInfo &info);
|
||||
void register_render_pass(VkRenderPass render_pass, Fossilize::Hash hash, const VkRenderPassCreateInfo2KHR &info);
|
||||
void register_descriptor_set_layout(VkDescriptorSetLayout layout, Fossilize::Hash hash, const VkDescriptorSetLayoutCreateInfo &info);
|
||||
void register_pipeline_layout(VkPipelineLayout layout, Fossilize::Hash hash, const VkPipelineLayoutCreateInfo &info);
|
||||
void register_shader_module(VkShaderModule module, Fossilize::Hash hash, const VkShaderModuleCreateInfo &info);
|
||||
void register_sampler(VkSampler sampler, Fossilize::Hash hash, const VkSamplerCreateInfo &info);
|
||||
void register_sampler_ycbcr_conversion(VkSamplerYcbcrConversion ycbcr, const VkSamplerYcbcrConversionCreateInfo &info);
|
||||
|
||||
struct RecorderState;
|
||||
std::unique_ptr<RecorderState> recorder_state;
|
||||
|
||||
struct ReplayerState;
|
||||
std::unique_ptr<ReplayerState> replayer_state;
|
||||
|
||||
void promote_write_cache_to_readonly() const;
|
||||
void promote_readonly_db_from_assets() const;
|
||||
|
||||
void init_pipeline_state(const Fossilize::FeatureFilter &filter,
|
||||
const VkPhysicalDeviceFeatures2 &pdf2,
|
||||
const VkApplicationInfo &application_info);
|
||||
void flush_pipeline_state();
|
||||
void block_until_shader_module_ready();
|
||||
void block_until_pipeline_ready();
|
||||
#endif
|
||||
|
||||
ImplementationWorkarounds workarounds;
|
||||
void init_workarounds();
|
||||
|
||||
void fill_buffer_sharing_indices(VkBufferCreateInfo &create_info, uint32_t *sharing_indices);
|
||||
|
||||
bool allocate_image_memory(DeviceAllocation *allocation, const ImageCreateInfo &info,
|
||||
VkImage image, VkImageTiling tiling);
|
||||
|
||||
void promote_read_write_caches_to_read_only();
|
||||
};
|
||||
|
||||
// A fairly complex helper used for async queue readbacks.
|
||||
// Typically used for things like headless backend which emulates WSI through readbacks + encode.
|
||||
struct OwnershipTransferInfo
|
||||
{
|
||||
CommandBuffer::Type old_queue;
|
||||
CommandBuffer::Type new_queue;
|
||||
VkImageLayout old_image_layout;
|
||||
VkImageLayout new_image_layout;
|
||||
VkPipelineStageFlags2 dst_pipeline_stage;
|
||||
VkAccessFlags2 dst_access;
|
||||
};
|
||||
|
||||
// For an image which was last accessed in old_queue, requests a command buffer
|
||||
// for new_queue. Commands will be enqueued as necessary in new_queue to ensure that a complete ownership
|
||||
// transfer has taken place.
|
||||
// If queue family for old_queue differs from new_queue, a release barrier is enqueued in old_queue.
|
||||
// In new_queue we perform either an acquire barrier or a simple pipeline barrier to change layout if required.
|
||||
// If semaphore is a valid handle, it will be waited on in either old_queue to perform release barrier
|
||||
// or new_queue depending on what is required.
|
||||
// If the image uses CONCURRENT sharing mode, acquire/release barriers are skipped.
|
||||
CommandBufferHandle request_command_buffer_with_ownership_transfer(
|
||||
Device &device,
|
||||
const Vulkan::Image &image,
|
||||
const OwnershipTransferInfo &info,
|
||||
const Vulkan::Semaphore &semaphore);
|
||||
|
||||
using DeviceHandle = Util::IntrusivePtr<Device>;
|
||||
}
|
||||
1036
external/parallel-rdp/parallel-rdp-standalone/vulkan/device_fossilize.cpp
vendored
Normal file
1036
external/parallel-rdp/parallel-rdp-standalone/vulkan/device_fossilize.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
70
external/parallel-rdp/parallel-rdp-standalone/vulkan/device_fossilize.hpp
vendored
Normal file
70
external/parallel-rdp/parallel-rdp-standalone/vulkan/device_fossilize.hpp
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "device.hpp"
|
||||
#include "thread_group.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
struct Device::RecorderState
|
||||
{
|
||||
RecorderState();
|
||||
~RecorderState();
|
||||
|
||||
std::unique_ptr<Fossilize::DatabaseInterface> db;
|
||||
Fossilize::StateRecorder recorder;
|
||||
std::atomic_bool recorder_ready;
|
||||
};
|
||||
|
||||
static constexpr unsigned NumTasks = 4;
|
||||
struct Device::ReplayerState
|
||||
{
|
||||
ReplayerState();
|
||||
~ReplayerState();
|
||||
|
||||
std::vector<Fossilize::Hash> module_hashes;
|
||||
std::vector<Fossilize::Hash> graphics_hashes;
|
||||
std::vector<Fossilize::Hash> compute_hashes;
|
||||
|
||||
Fossilize::StateReplayer base_replayer;
|
||||
Fossilize::StateReplayer graphics_replayer;
|
||||
Fossilize::StateReplayer compute_replayer;
|
||||
Fossilize::FeatureFilter *feature_filter = nullptr;
|
||||
std::unique_ptr<Fossilize::DatabaseInterface> db;
|
||||
Granite::TaskGroupHandle complete;
|
||||
Granite::TaskGroupHandle module_ready;
|
||||
Granite::TaskGroupHandle pipeline_ready;
|
||||
std::vector<std::pair<Fossilize::Hash, VkGraphicsPipelineCreateInfo *>> graphics_pipelines;
|
||||
std::vector<std::pair<Fossilize::Hash, VkComputePipelineCreateInfo *>> compute_pipelines;
|
||||
|
||||
struct
|
||||
{
|
||||
std::atomic_uint32_t pipelines;
|
||||
std::atomic_uint32_t modules;
|
||||
std::atomic_uint32_t prepare;
|
||||
uint32_t num_pipelines = 0;
|
||||
uint32_t num_modules = 0;
|
||||
} progress;
|
||||
};
|
||||
}
|
||||
72
external/parallel-rdp/parallel-rdp-standalone/vulkan/event_manager.cpp
vendored
Normal file
72
external/parallel-rdp/parallel-rdp-standalone/vulkan/event_manager.cpp
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "event_manager.hpp"
|
||||
#include "device.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
EventManager::~EventManager()
|
||||
{
|
||||
if (!workaround)
|
||||
for (auto &event : events)
|
||||
table->vkDestroyEvent(device->get_device(), event, nullptr);
|
||||
}
|
||||
|
||||
void EventManager::recycle(VkEvent event)
|
||||
{
|
||||
if (!workaround && event != VK_NULL_HANDLE)
|
||||
{
|
||||
table->vkResetEvent(device->get_device(), event);
|
||||
events.push_back(event);
|
||||
}
|
||||
}
|
||||
|
||||
VkEvent EventManager::request_cleared_event()
|
||||
{
|
||||
if (workaround)
|
||||
{
|
||||
// Can't use reinterpret_cast because of MSVC.
|
||||
return (VkEvent) ++workaround_counter;
|
||||
}
|
||||
else if (events.empty())
|
||||
{
|
||||
VkEvent event;
|
||||
VkEventCreateInfo info = { VK_STRUCTURE_TYPE_EVENT_CREATE_INFO };
|
||||
table->vkCreateEvent(device->get_device(), &info, nullptr, &event);
|
||||
return event;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto event = events.back();
|
||||
events.pop_back();
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
void EventManager::init(Device *device_)
|
||||
{
|
||||
device = device_;
|
||||
table = &device->get_device_table();
|
||||
workaround = device_->get_workarounds().emulate_event_as_pipeline_barrier;
|
||||
}
|
||||
}
|
||||
47
external/parallel-rdp/parallel-rdp-standalone/vulkan/event_manager.hpp
vendored
Normal file
47
external/parallel-rdp/parallel-rdp-standalone/vulkan/event_manager.hpp
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vulkan_headers.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class Device;
|
||||
class EventManager
|
||||
{
|
||||
public:
|
||||
void init(Device *device);
|
||||
~EventManager();
|
||||
|
||||
VkEvent request_cleared_event();
|
||||
void recycle(VkEvent event);
|
||||
|
||||
private:
|
||||
Device *device = nullptr;
|
||||
const VolkDeviceTable *table = nullptr;
|
||||
std::vector<VkEvent> events;
|
||||
uint64_t workaround_counter = 0;
|
||||
bool workaround = false;
|
||||
};
|
||||
}
|
||||
124
external/parallel-rdp/parallel-rdp-standalone/vulkan/fence.cpp
vendored
Normal file
124
external/parallel-rdp/parallel-rdp-standalone/vulkan/fence.cpp
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "fence.hpp"
|
||||
#include "device.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
FenceHolder::~FenceHolder()
|
||||
{
|
||||
if (fence != VK_NULL_HANDLE)
|
||||
{
|
||||
if (internal_sync)
|
||||
device->reset_fence_nolock(fence, observed_wait);
|
||||
else
|
||||
device->reset_fence(fence, observed_wait);
|
||||
}
|
||||
}
|
||||
|
||||
const VkFence &FenceHolder::get_fence() const
|
||||
{
|
||||
return fence;
|
||||
}
|
||||
|
||||
void FenceHolder::wait()
|
||||
{
|
||||
auto &table = device->get_device_table();
|
||||
|
||||
// Waiting for the same VkFence in parallel is not allowed, and there seems to be some shenanigans on Intel
|
||||
// when waiting for a timeline semaphore in parallel with same value as well.
|
||||
std::lock_guard<std::mutex> holder{lock};
|
||||
|
||||
if (observed_wait)
|
||||
return;
|
||||
|
||||
if (timeline_value != 0)
|
||||
{
|
||||
VK_ASSERT(timeline_semaphore);
|
||||
VkSemaphoreWaitInfo info = { VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO };
|
||||
info.semaphoreCount = 1;
|
||||
info.pSemaphores = &timeline_semaphore;
|
||||
info.pValues = &timeline_value;
|
||||
if (table.vkWaitSemaphores(device->get_device(), &info, UINT64_MAX) != VK_SUCCESS)
|
||||
LOGE("Failed to wait for timeline semaphore!\n");
|
||||
else
|
||||
observed_wait = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (table.vkWaitForFences(device->get_device(), 1, &fence, VK_TRUE, UINT64_MAX) != VK_SUCCESS)
|
||||
LOGE("Failed to wait for fence!\n");
|
||||
else
|
||||
observed_wait = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool FenceHolder::wait_timeout(uint64_t timeout)
|
||||
{
|
||||
bool ret;
|
||||
auto &table = device->get_device_table();
|
||||
|
||||
// Waiting for the same VkFence in parallel is not allowed, and there seems to be some shenanigans on Intel
|
||||
// when waiting for a timeline semaphore in parallel with same value as well.
|
||||
std::lock_guard<std::mutex> holder{lock};
|
||||
|
||||
if (observed_wait)
|
||||
return true;
|
||||
|
||||
if (timeline_value != 0)
|
||||
{
|
||||
VK_ASSERT(timeline_semaphore);
|
||||
|
||||
if (timeout == 0)
|
||||
{
|
||||
uint64_t current_value = 0;
|
||||
ret = table.vkGetSemaphoreCounterValue(device->get_device(), timeline_semaphore, ¤t_value) == VK_SUCCESS &&
|
||||
current_value >= timeline_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
VkSemaphoreWaitInfo info = {VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO};
|
||||
info.semaphoreCount = 1;
|
||||
info.pSemaphores = &timeline_semaphore;
|
||||
info.pValues = &timeline_value;
|
||||
ret = table.vkWaitSemaphores(device->get_device(), &info, timeout) == VK_SUCCESS;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (timeout == 0)
|
||||
ret = table.vkGetFenceStatus(device->get_device(), fence) == VK_SUCCESS;
|
||||
else
|
||||
ret = table.vkWaitForFences(device->get_device(), 1, &fence, VK_TRUE, timeout) == VK_SUCCESS;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
observed_wait = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void FenceHolderDeleter::operator()(Vulkan::FenceHolder *fence)
|
||||
{
|
||||
fence->device->handle_pool.fences.free(fence);
|
||||
}
|
||||
}
|
||||
81
external/parallel-rdp/parallel-rdp-standalone/vulkan/fence.hpp
vendored
Normal file
81
external/parallel-rdp/parallel-rdp-standalone/vulkan/fence.hpp
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vulkan_common.hpp"
|
||||
#include "vulkan_headers.hpp"
|
||||
#include "object_pool.hpp"
|
||||
#include "cookie.hpp"
|
||||
#include <mutex>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class Device;
|
||||
|
||||
class FenceHolder;
|
||||
struct FenceHolderDeleter
|
||||
{
|
||||
void operator()(FenceHolder *fence);
|
||||
};
|
||||
|
||||
class FenceHolder : public Util::IntrusivePtrEnabled<FenceHolder, FenceHolderDeleter, HandleCounter>, public InternalSyncEnabled
|
||||
{
|
||||
public:
|
||||
friend struct FenceHolderDeleter;
|
||||
friend class WSI;
|
||||
|
||||
~FenceHolder();
|
||||
void wait();
|
||||
bool wait_timeout(uint64_t nsec);
|
||||
|
||||
private:
|
||||
friend class Util::ObjectPool<FenceHolder>;
|
||||
FenceHolder(Device *device_, VkFence fence_)
|
||||
: device(device_),
|
||||
fence(fence_),
|
||||
timeline_semaphore(VK_NULL_HANDLE),
|
||||
timeline_value(0)
|
||||
{
|
||||
}
|
||||
|
||||
FenceHolder(Device *device_, uint64_t value, VkSemaphore timeline_semaphore_)
|
||||
: device(device_),
|
||||
fence(VK_NULL_HANDLE),
|
||||
timeline_semaphore(timeline_semaphore_),
|
||||
timeline_value(value)
|
||||
{
|
||||
VK_ASSERT(value > 0);
|
||||
}
|
||||
|
||||
const VkFence &get_fence() const;
|
||||
|
||||
Device *device;
|
||||
VkFence fence;
|
||||
VkSemaphore timeline_semaphore;
|
||||
uint64_t timeline_value;
|
||||
bool observed_wait = false;
|
||||
std::mutex lock;
|
||||
};
|
||||
|
||||
using Fence = Util::IntrusivePtr<FenceHolder>;
|
||||
}
|
||||
61
external/parallel-rdp/parallel-rdp-standalone/vulkan/fence_manager.cpp
vendored
Normal file
61
external/parallel-rdp/parallel-rdp-standalone/vulkan/fence_manager.cpp
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "fence_manager.hpp"
|
||||
#include "device.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
void FenceManager::init(Device *device_)
|
||||
{
|
||||
device = device_;
|
||||
table = &device->get_device_table();
|
||||
}
|
||||
|
||||
VkFence FenceManager::request_cleared_fence()
|
||||
{
|
||||
if (!fences.empty())
|
||||
{
|
||||
auto ret = fences.back();
|
||||
fences.pop_back();
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
VkFence fence;
|
||||
VkFenceCreateInfo info = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
|
||||
table->vkCreateFence(device->get_device(), &info, nullptr, &fence);
|
||||
return fence;
|
||||
}
|
||||
}
|
||||
|
||||
void FenceManager::recycle_fence(VkFence fence)
|
||||
{
|
||||
fences.push_back(fence);
|
||||
}
|
||||
|
||||
FenceManager::~FenceManager()
|
||||
{
|
||||
for (auto &fence : fences)
|
||||
table->vkDestroyFence(device->get_device(), fence, nullptr);
|
||||
}
|
||||
}
|
||||
45
external/parallel-rdp/parallel-rdp-standalone/vulkan/fence_manager.hpp
vendored
Normal file
45
external/parallel-rdp/parallel-rdp-standalone/vulkan/fence_manager.hpp
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vulkan_headers.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class Device;
|
||||
class FenceManager
|
||||
{
|
||||
public:
|
||||
void init(Device *device);
|
||||
~FenceManager();
|
||||
|
||||
VkFence request_cleared_fence();
|
||||
void recycle_fence(VkFence fence);
|
||||
|
||||
private:
|
||||
Device *device = nullptr;
|
||||
const VolkDeviceTable *table = nullptr;
|
||||
std::vector<VkFence> fences;
|
||||
};
|
||||
}
|
||||
386
external/parallel-rdp/parallel-rdp-standalone/vulkan/format.hpp
vendored
Normal file
386
external/parallel-rdp/parallel-rdp-standalone/vulkan/format.hpp
vendored
Normal file
@@ -0,0 +1,386 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vulkan_headers.hpp"
|
||||
#include "texture/texture_format.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
enum class FormatCompressionType
|
||||
{
|
||||
Uncompressed,
|
||||
BC,
|
||||
ETC,
|
||||
ASTC
|
||||
};
|
||||
|
||||
static inline FormatCompressionType format_compression_type(VkFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
|
||||
case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
|
||||
case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
|
||||
case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
|
||||
case VK_FORMAT_BC2_SRGB_BLOCK:
|
||||
case VK_FORMAT_BC2_UNORM_BLOCK:
|
||||
case VK_FORMAT_BC3_SRGB_BLOCK:
|
||||
case VK_FORMAT_BC3_UNORM_BLOCK:
|
||||
case VK_FORMAT_BC4_UNORM_BLOCK:
|
||||
case VK_FORMAT_BC4_SNORM_BLOCK:
|
||||
case VK_FORMAT_BC5_UNORM_BLOCK:
|
||||
case VK_FORMAT_BC5_SNORM_BLOCK:
|
||||
case VK_FORMAT_BC6H_SFLOAT_BLOCK:
|
||||
case VK_FORMAT_BC6H_UFLOAT_BLOCK:
|
||||
case VK_FORMAT_BC7_SRGB_BLOCK:
|
||||
case VK_FORMAT_BC7_UNORM_BLOCK:
|
||||
return FormatCompressionType::BC;
|
||||
|
||||
case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
|
||||
case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
|
||||
case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
|
||||
case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
|
||||
case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
|
||||
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
|
||||
case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
|
||||
case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
|
||||
case VK_FORMAT_EAC_R11_SNORM_BLOCK:
|
||||
case VK_FORMAT_EAC_R11_UNORM_BLOCK:
|
||||
return FormatCompressionType::ETC;
|
||||
|
||||
#define astc_fmt(w, h) \
|
||||
case VK_FORMAT_ASTC_##w##x##h##_UNORM_BLOCK: \
|
||||
case VK_FORMAT_ASTC_##w##x##h##_SRGB_BLOCK: \
|
||||
case VK_FORMAT_ASTC_##w##x##h##_SFLOAT_BLOCK_EXT
|
||||
astc_fmt(4, 4):
|
||||
astc_fmt(5, 4):
|
||||
astc_fmt(5, 5):
|
||||
astc_fmt(6, 5):
|
||||
astc_fmt(6, 6):
|
||||
astc_fmt(8, 5):
|
||||
astc_fmt(8, 6):
|
||||
astc_fmt(8, 8):
|
||||
astc_fmt(10, 5):
|
||||
astc_fmt(10, 6):
|
||||
astc_fmt(10, 8):
|
||||
astc_fmt(10, 10):
|
||||
astc_fmt(12, 10):
|
||||
astc_fmt(12, 12):
|
||||
return FormatCompressionType::ASTC;
|
||||
#undef astc_fmt
|
||||
|
||||
default:
|
||||
return FormatCompressionType::Uncompressed;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool format_is_compressed_hdr(VkFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
#define astc_fmt(w, h) case VK_FORMAT_ASTC_##w##x##h##_SFLOAT_BLOCK_EXT
|
||||
astc_fmt(4, 4):
|
||||
astc_fmt(5, 4):
|
||||
astc_fmt(5, 5):
|
||||
astc_fmt(6, 5):
|
||||
astc_fmt(6, 6):
|
||||
astc_fmt(8, 5):
|
||||
astc_fmt(8, 6):
|
||||
astc_fmt(8, 8):
|
||||
astc_fmt(10, 5):
|
||||
astc_fmt(10, 6):
|
||||
astc_fmt(10, 8):
|
||||
astc_fmt(10, 10):
|
||||
astc_fmt(12, 10):
|
||||
astc_fmt(12, 12):
|
||||
#undef astc_fmt
|
||||
return true;
|
||||
|
||||
case VK_FORMAT_BC6H_SFLOAT_BLOCK:
|
||||
case VK_FORMAT_BC6H_UFLOAT_BLOCK:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool format_is_srgb(VkFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
|
||||
case VK_FORMAT_R8G8B8A8_SRGB:
|
||||
case VK_FORMAT_B8G8R8A8_SRGB:
|
||||
case VK_FORMAT_R8_SRGB:
|
||||
case VK_FORMAT_R8G8_SRGB:
|
||||
case VK_FORMAT_R8G8B8_SRGB:
|
||||
case VK_FORMAT_B8G8R8_SRGB:
|
||||
case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
|
||||
case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
|
||||
case VK_FORMAT_BC2_SRGB_BLOCK:
|
||||
case VK_FORMAT_BC3_SRGB_BLOCK:
|
||||
case VK_FORMAT_BC7_SRGB_BLOCK:
|
||||
case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
|
||||
case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
|
||||
case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool format_has_depth_aspect(VkFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case VK_FORMAT_D16_UNORM:
|
||||
case VK_FORMAT_D16_UNORM_S8_UINT:
|
||||
case VK_FORMAT_D24_UNORM_S8_UINT:
|
||||
case VK_FORMAT_D32_SFLOAT:
|
||||
case VK_FORMAT_X8_D24_UNORM_PACK32:
|
||||
case VK_FORMAT_D32_SFLOAT_S8_UINT:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool format_has_stencil_aspect(VkFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case VK_FORMAT_D16_UNORM_S8_UINT:
|
||||
case VK_FORMAT_D24_UNORM_S8_UINT:
|
||||
case VK_FORMAT_D32_SFLOAT_S8_UINT:
|
||||
case VK_FORMAT_S8_UINT:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool format_has_depth_or_stencil_aspect(VkFormat format)
|
||||
{
|
||||
return format_has_depth_aspect(format) || format_has_stencil_aspect(format);
|
||||
}
|
||||
|
||||
static inline VkImageAspectFlags format_to_aspect_mask(VkFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case VK_FORMAT_UNDEFINED:
|
||||
return 0;
|
||||
|
||||
case VK_FORMAT_S8_UINT:
|
||||
return VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
|
||||
case VK_FORMAT_D16_UNORM_S8_UINT:
|
||||
case VK_FORMAT_D24_UNORM_S8_UINT:
|
||||
case VK_FORMAT_D32_SFLOAT_S8_UINT:
|
||||
return VK_IMAGE_ASPECT_STENCIL_BIT | VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
|
||||
case VK_FORMAT_D16_UNORM:
|
||||
case VK_FORMAT_D32_SFLOAT:
|
||||
case VK_FORMAT_X8_D24_UNORM_PACK32:
|
||||
return VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
|
||||
default:
|
||||
return VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void format_align_dim(VkFormat format, uint32_t &width, uint32_t &height)
|
||||
{
|
||||
uint32_t align_width, align_height;
|
||||
TextureFormatLayout::format_block_dim(format, align_width, align_height);
|
||||
width = ((width + align_width - 1) / align_width) * align_width;
|
||||
height = ((height + align_height - 1) / align_height) * align_height;
|
||||
}
|
||||
|
||||
static inline void format_num_blocks(VkFormat format, uint32_t &width, uint32_t &height)
|
||||
{
|
||||
uint32_t align_width, align_height;
|
||||
TextureFormatLayout::format_block_dim(format, align_width, align_height);
|
||||
width = (width + align_width - 1) / align_width;
|
||||
height = (height + align_height - 1) / align_height;
|
||||
}
|
||||
|
||||
static inline VkDeviceSize format_get_layer_size(VkFormat format, VkImageAspectFlags aspect, unsigned width, unsigned height, unsigned depth)
|
||||
{
|
||||
uint32_t blocks_x = width;
|
||||
uint32_t blocks_y = height;
|
||||
format_num_blocks(format, blocks_x, blocks_y);
|
||||
format_align_dim(format, width, height);
|
||||
|
||||
VkDeviceSize size = VkDeviceSize(TextureFormatLayout::format_block_size(format, aspect)) * depth * blocks_x * blocks_y;
|
||||
return size;
|
||||
}
|
||||
|
||||
static inline unsigned format_ycbcr_num_planes(VkFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
|
||||
case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:
|
||||
case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:
|
||||
case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
|
||||
case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:
|
||||
case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
|
||||
case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
|
||||
case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16:
|
||||
case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16:
|
||||
case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
|
||||
case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16:
|
||||
case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16:
|
||||
return 3;
|
||||
|
||||
case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
|
||||
case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:
|
||||
case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
|
||||
case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:
|
||||
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
|
||||
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16:
|
||||
case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
|
||||
case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:
|
||||
return 2;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void format_ycbcr_downsample_dimensions(VkFormat format, VkImageAspectFlags aspect, uint32_t &width, uint32_t &height)
|
||||
{
|
||||
if (aspect == VK_IMAGE_ASPECT_PLANE_0_BIT)
|
||||
return;
|
||||
|
||||
switch (format)
|
||||
{
|
||||
#define fmt(x, sub0, sub1) \
|
||||
case VK_FORMAT_##x: \
|
||||
width >>= sub0; \
|
||||
height >>= sub1; \
|
||||
break
|
||||
|
||||
fmt(G8_B8_R8_3PLANE_420_UNORM, 1, 1);
|
||||
fmt(G8_B8R8_2PLANE_420_UNORM, 1, 1);
|
||||
fmt(G8_B8_R8_3PLANE_422_UNORM, 1, 0);
|
||||
fmt(G8_B8R8_2PLANE_422_UNORM, 1, 0);
|
||||
fmt(G8_B8_R8_3PLANE_444_UNORM, 0, 0);
|
||||
|
||||
fmt(G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16, 1, 1);
|
||||
fmt(G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16, 1, 0);
|
||||
fmt(G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, 0, 0);
|
||||
fmt(G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, 1, 1);
|
||||
fmt(G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, 1, 0);
|
||||
|
||||
fmt(G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16, 1, 1);
|
||||
fmt(G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16, 1, 0);
|
||||
fmt(G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16, 0, 0);
|
||||
fmt(G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, 1, 1);
|
||||
fmt(G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16, 1, 0);
|
||||
|
||||
fmt(G16_B16_R16_3PLANE_420_UNORM, 1, 1);
|
||||
fmt(G16_B16_R16_3PLANE_422_UNORM, 1, 0);
|
||||
fmt(G16_B16_R16_3PLANE_444_UNORM, 0, 0);
|
||||
fmt(G16_B16R16_2PLANE_420_UNORM, 1, 1);
|
||||
fmt(G16_B16R16_2PLANE_422_UNORM, 1, 0);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#undef fmt
|
||||
}
|
||||
|
||||
static inline bool format_supports_storage_image_read_write_without_format(VkFormat format)
|
||||
{
|
||||
/* from https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html#formats-without-shader-storage-format */
|
||||
static const VkFormat supported_formats[] =
|
||||
{
|
||||
VK_FORMAT_R8G8B8A8_UNORM,
|
||||
VK_FORMAT_R8G8B8A8_SNORM,
|
||||
VK_FORMAT_R8G8B8A8_UINT,
|
||||
VK_FORMAT_R8G8B8A8_SINT,
|
||||
VK_FORMAT_R32_UINT,
|
||||
VK_FORMAT_R32_SINT,
|
||||
VK_FORMAT_R32_SFLOAT,
|
||||
VK_FORMAT_R32G32_UINT,
|
||||
VK_FORMAT_R32G32_SINT,
|
||||
VK_FORMAT_R32G32_SFLOAT,
|
||||
VK_FORMAT_R32G32B32A32_UINT,
|
||||
VK_FORMAT_R32G32B32A32_SINT,
|
||||
VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
VK_FORMAT_R16G16B16A16_UINT,
|
||||
VK_FORMAT_R16G16B16A16_SINT,
|
||||
VK_FORMAT_R16G16B16A16_SFLOAT,
|
||||
VK_FORMAT_R16G16_SFLOAT,
|
||||
VK_FORMAT_B10G11R11_UFLOAT_PACK32,
|
||||
VK_FORMAT_R16_SFLOAT,
|
||||
VK_FORMAT_R16G16B16A16_UNORM,
|
||||
VK_FORMAT_A2B10G10R10_UNORM_PACK32,
|
||||
VK_FORMAT_R16G16_UNORM,
|
||||
VK_FORMAT_R8G8_UNORM,
|
||||
VK_FORMAT_R16_UNORM,
|
||||
VK_FORMAT_R8_UNORM,
|
||||
VK_FORMAT_R16G16B16A16_SNORM,
|
||||
VK_FORMAT_R16G16_SNORM,
|
||||
VK_FORMAT_R8G8_SNORM,
|
||||
VK_FORMAT_R16_SNORM,
|
||||
VK_FORMAT_R8_SNORM,
|
||||
VK_FORMAT_R16G16_SINT,
|
||||
VK_FORMAT_R8G8_SINT,
|
||||
VK_FORMAT_R16_SINT,
|
||||
VK_FORMAT_R8_SINT,
|
||||
VK_FORMAT_A2B10G10R10_UINT_PACK32,
|
||||
VK_FORMAT_R16G16_UINT,
|
||||
VK_FORMAT_R8G8_UINT,
|
||||
VK_FORMAT_R16_UINT,
|
||||
VK_FORMAT_R8_UINT,
|
||||
};
|
||||
|
||||
for (auto fmt : supported_formats)
|
||||
if (fmt == format)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
244
external/parallel-rdp/parallel-rdp-standalone/vulkan/image.cpp
vendored
Normal file
244
external/parallel-rdp/parallel-rdp-standalone/vulkan/image.cpp
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "image.hpp"
|
||||
#include "device.hpp"
|
||||
#include "buffer.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
ImageView::ImageView(Device *device_, VkImageView view_, const ImageViewCreateInfo &info_)
|
||||
: Cookie(device_)
|
||||
, device(device_)
|
||||
, view(view_)
|
||||
, info(info_)
|
||||
{
|
||||
}
|
||||
|
||||
VkImageView ImageView::get_render_target_view(unsigned layer) const
|
||||
{
|
||||
// Transient images just have one layer.
|
||||
if (info.image->get_create_info().domain == ImageDomain::Transient)
|
||||
return view;
|
||||
|
||||
VK_ASSERT(layer < get_create_info().layers);
|
||||
|
||||
if (render_target_views.empty())
|
||||
return view;
|
||||
else
|
||||
{
|
||||
VK_ASSERT(layer < render_target_views.size());
|
||||
return render_target_views[layer];
|
||||
}
|
||||
}
|
||||
|
||||
ImageView::~ImageView()
|
||||
{
|
||||
if (internal_sync)
|
||||
{
|
||||
device->destroy_image_view_nolock(view);
|
||||
if (depth_view != VK_NULL_HANDLE)
|
||||
device->destroy_image_view_nolock(depth_view);
|
||||
if (stencil_view != VK_NULL_HANDLE)
|
||||
device->destroy_image_view_nolock(stencil_view);
|
||||
if (unorm_view != VK_NULL_HANDLE)
|
||||
device->destroy_image_view_nolock(unorm_view);
|
||||
if (srgb_view != VK_NULL_HANDLE)
|
||||
device->destroy_image_view_nolock(srgb_view);
|
||||
|
||||
for (auto &v : render_target_views)
|
||||
device->destroy_image_view_nolock(v);
|
||||
}
|
||||
else
|
||||
{
|
||||
device->destroy_image_view(view);
|
||||
if (depth_view != VK_NULL_HANDLE)
|
||||
device->destroy_image_view(depth_view);
|
||||
if (stencil_view != VK_NULL_HANDLE)
|
||||
device->destroy_image_view(stencil_view);
|
||||
if (unorm_view != VK_NULL_HANDLE)
|
||||
device->destroy_image_view(unorm_view);
|
||||
if (srgb_view != VK_NULL_HANDLE)
|
||||
device->destroy_image_view(srgb_view);
|
||||
|
||||
for (auto &v : render_target_views)
|
||||
device->destroy_image_view(v);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned ImageView::get_view_width() const
|
||||
{
|
||||
return info.image->get_width(info.base_level);
|
||||
}
|
||||
|
||||
unsigned ImageView::get_view_height() const
|
||||
{
|
||||
return info.image->get_height(info.base_level);
|
||||
}
|
||||
|
||||
unsigned ImageView::get_view_depth() const
|
||||
{
|
||||
return info.image->get_depth(info.base_level);
|
||||
}
|
||||
|
||||
Image::Image(Device *device_, VkImage image_, VkImageView default_view, const DeviceAllocation &alloc_,
|
||||
const ImageCreateInfo &create_info_, VkImageViewType view_type)
|
||||
: Cookie(device_)
|
||||
, device(device_)
|
||||
, image(image_)
|
||||
, alloc(alloc_)
|
||||
, create_info(create_info_)
|
||||
{
|
||||
if (default_view != VK_NULL_HANDLE)
|
||||
{
|
||||
ImageViewCreateInfo info;
|
||||
info.image = this;
|
||||
info.view_type = view_type;
|
||||
info.format = create_info.format;
|
||||
info.base_level = 0;
|
||||
info.levels = create_info.levels;
|
||||
info.base_layer = 0;
|
||||
info.layers = create_info.layers;
|
||||
view = ImageViewHandle(device->handle_pool.image_views.allocate(device, default_view, info));
|
||||
}
|
||||
}
|
||||
|
||||
DeviceAllocation Image::take_allocation_ownership()
|
||||
{
|
||||
DeviceAllocation ret = {};
|
||||
std::swap(ret, alloc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ExternalHandle Image::export_handle()
|
||||
{
|
||||
return alloc.export_handle(*device);
|
||||
}
|
||||
|
||||
void Image::disown_image()
|
||||
{
|
||||
owns_image = false;
|
||||
}
|
||||
|
||||
void Image::disown_memory_allocation()
|
||||
{
|
||||
owns_memory_allocation = false;
|
||||
}
|
||||
|
||||
Image::~Image()
|
||||
{
|
||||
if (owns_image)
|
||||
{
|
||||
if (internal_sync)
|
||||
device->destroy_image_nolock(image);
|
||||
else
|
||||
device->destroy_image(image);
|
||||
}
|
||||
|
||||
if (alloc.get_memory() && owns_memory_allocation)
|
||||
{
|
||||
if (internal_sync)
|
||||
device->free_memory_nolock(alloc);
|
||||
else
|
||||
device->free_memory(alloc);
|
||||
}
|
||||
}
|
||||
|
||||
const Buffer &LinearHostImage::get_host_visible_buffer() const
|
||||
{
|
||||
return *cpu_image;
|
||||
}
|
||||
|
||||
bool LinearHostImage::need_staging_copy() const
|
||||
{
|
||||
return gpu_image->get_create_info().domain != ImageDomain::LinearHostCached &&
|
||||
gpu_image->get_create_info().domain != ImageDomain::LinearHost;
|
||||
}
|
||||
|
||||
const DeviceAllocation &LinearHostImage::get_host_visible_allocation() const
|
||||
{
|
||||
return need_staging_copy() ? cpu_image->get_allocation() : gpu_image->get_allocation();
|
||||
}
|
||||
|
||||
const ImageView &LinearHostImage::get_view() const
|
||||
{
|
||||
return gpu_image->get_view();
|
||||
}
|
||||
|
||||
const Image &LinearHostImage::get_image() const
|
||||
{
|
||||
return *gpu_image;
|
||||
}
|
||||
|
||||
size_t LinearHostImage::get_offset() const
|
||||
{
|
||||
return row_offset;
|
||||
}
|
||||
|
||||
size_t LinearHostImage::get_row_pitch_bytes() const
|
||||
{
|
||||
return row_pitch;
|
||||
}
|
||||
|
||||
VkPipelineStageFlags2 LinearHostImage::get_used_pipeline_stages() const
|
||||
{
|
||||
return stages;
|
||||
}
|
||||
|
||||
LinearHostImage::LinearHostImage(Device *device_, ImageHandle gpu_image_, BufferHandle cpu_image_, VkPipelineStageFlags2 stages_)
|
||||
: device(device_), gpu_image(std::move(gpu_image_)), cpu_image(std::move(cpu_image_)), stages(stages_)
|
||||
{
|
||||
if (gpu_image->get_create_info().domain == ImageDomain::LinearHostCached ||
|
||||
gpu_image->get_create_info().domain == ImageDomain::LinearHost)
|
||||
{
|
||||
VkImageSubresource sub = {};
|
||||
sub.aspectMask = format_to_aspect_mask(gpu_image->get_format());
|
||||
VkSubresourceLayout layout;
|
||||
|
||||
auto &table = device_->get_device_table();
|
||||
table.vkGetImageSubresourceLayout(device->get_device(), gpu_image->get_image(), &sub, &layout);
|
||||
row_pitch = layout.rowPitch;
|
||||
row_offset = layout.offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
row_pitch = gpu_image->get_width() * TextureFormatLayout::format_block_size(gpu_image->get_format(),
|
||||
format_to_aspect_mask(gpu_image->get_format()));
|
||||
row_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ImageViewDeleter::operator()(ImageView *view)
|
||||
{
|
||||
view->device->handle_pool.image_views.free(view);
|
||||
}
|
||||
|
||||
void ImageDeleter::operator()(Image *image)
|
||||
{
|
||||
image->device->handle_pool.images.free(image);
|
||||
}
|
||||
|
||||
void LinearHostImageDeleter::operator()(LinearHostImage *image)
|
||||
{
|
||||
image->device->handle_pool.linear_images.free(image);
|
||||
}
|
||||
}
|
||||
581
external/parallel-rdp/parallel-rdp-standalone/vulkan/image.hpp
vendored
Normal file
581
external/parallel-rdp/parallel-rdp-standalone/vulkan/image.hpp
vendored
Normal file
@@ -0,0 +1,581 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cookie.hpp"
|
||||
#include "format.hpp"
|
||||
#include "vulkan_common.hpp"
|
||||
#include "memory_allocator.hpp"
|
||||
#include "vulkan_headers.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class Device;
|
||||
|
||||
static inline uint32_t image_num_miplevels(const VkExtent3D &extent)
|
||||
{
|
||||
uint32_t size = std::max<uint32_t>(std::max<uint32_t>(extent.width, extent.height), extent.depth);
|
||||
return Util::floor_log2(size) + 1;
|
||||
}
|
||||
|
||||
static inline VkFormatFeatureFlags image_usage_to_features(VkImageUsageFlags usage)
|
||||
{
|
||||
VkFormatFeatureFlags flags = 0;
|
||||
if (usage & VK_IMAGE_USAGE_SAMPLED_BIT)
|
||||
flags |= VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
|
||||
if (usage & VK_IMAGE_USAGE_STORAGE_BIT)
|
||||
flags |= VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT;
|
||||
if (usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
|
||||
flags |= VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
|
||||
if (usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
|
||||
flags |= VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
struct ImageInitialData
|
||||
{
|
||||
const void *data;
|
||||
unsigned row_length;
|
||||
unsigned image_height;
|
||||
};
|
||||
|
||||
enum ImageMiscFlagBits
|
||||
{
|
||||
IMAGE_MISC_GENERATE_MIPS_BIT = 1 << 0,
|
||||
IMAGE_MISC_FORCE_ARRAY_BIT = 1 << 1,
|
||||
IMAGE_MISC_MUTABLE_SRGB_BIT = 1 << 2,
|
||||
IMAGE_MISC_CONCURRENT_QUEUE_GRAPHICS_BIT = 1 << 3,
|
||||
IMAGE_MISC_CONCURRENT_QUEUE_ASYNC_COMPUTE_BIT = 1 << 4,
|
||||
IMAGE_MISC_CONCURRENT_QUEUE_ASYNC_TRANSFER_BIT = 1 << 6,
|
||||
IMAGE_MISC_CONCURRENT_QUEUE_VIDEO_DECODE_BIT = 1 << 7,
|
||||
IMAGE_MISC_VERIFY_FORMAT_FEATURE_SAMPLED_LINEAR_FILTER_BIT = 1 << 8,
|
||||
IMAGE_MISC_LINEAR_IMAGE_IGNORE_DEVICE_LOCAL_BIT = 1 << 9,
|
||||
IMAGE_MISC_FORCE_NO_DEDICATED_BIT = 1 << 10,
|
||||
IMAGE_MISC_NO_DEFAULT_VIEWS_BIT = 1 << 11,
|
||||
IMAGE_MISC_EXTERNAL_MEMORY_BIT = 1 << 12,
|
||||
IMAGE_MISC_CONCURRENT_QUEUE_VIDEO_ENCODE_BIT = 1 << 13,
|
||||
IMAGE_MISC_CONCURRENT_QUEUE_VIDEO_DUPLEX =
|
||||
IMAGE_MISC_CONCURRENT_QUEUE_VIDEO_DECODE_BIT |
|
||||
IMAGE_MISC_CONCURRENT_QUEUE_VIDEO_ENCODE_BIT,
|
||||
};
|
||||
using ImageMiscFlags = uint32_t;
|
||||
|
||||
enum ImageViewMiscFlagBits
|
||||
{
|
||||
IMAGE_VIEW_MISC_FORCE_ARRAY_BIT = 1 << 0
|
||||
};
|
||||
using ImageViewMiscFlags = uint32_t;
|
||||
|
||||
class Image;
|
||||
class ImmutableYcbcrConversion;
|
||||
|
||||
struct ImageViewCreateInfo
|
||||
{
|
||||
const Image *image = nullptr;
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
unsigned base_level = 0;
|
||||
unsigned levels = VK_REMAINING_MIP_LEVELS;
|
||||
unsigned base_layer = 0;
|
||||
unsigned layers = VK_REMAINING_ARRAY_LAYERS;
|
||||
VkImageViewType view_type = VK_IMAGE_VIEW_TYPE_MAX_ENUM;
|
||||
ImageViewMiscFlags misc = 0;
|
||||
VkComponentMapping swizzle = {
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
};
|
||||
VkImageAspectFlags aspect = 0;
|
||||
const ImmutableYcbcrConversion *ycbcr_conversion = nullptr;
|
||||
};
|
||||
|
||||
class ImageView;
|
||||
|
||||
struct ImageViewDeleter
|
||||
{
|
||||
void operator()(ImageView *view);
|
||||
};
|
||||
|
||||
class ImageView : public Util::IntrusivePtrEnabled<ImageView, ImageViewDeleter, HandleCounter>,
|
||||
public Cookie, public InternalSyncEnabled
|
||||
{
|
||||
public:
|
||||
friend struct ImageViewDeleter;
|
||||
|
||||
ImageView(Device *device, VkImageView view, const ImageViewCreateInfo &info);
|
||||
|
||||
~ImageView();
|
||||
|
||||
void set_alt_views(VkImageView depth, VkImageView stencil)
|
||||
{
|
||||
VK_ASSERT(depth_view == VK_NULL_HANDLE);
|
||||
VK_ASSERT(stencil_view == VK_NULL_HANDLE);
|
||||
depth_view = depth;
|
||||
stencil_view = stencil;
|
||||
}
|
||||
|
||||
void set_render_target_views(std::vector<VkImageView> views)
|
||||
{
|
||||
VK_ASSERT(render_target_views.empty());
|
||||
render_target_views = std::move(views);
|
||||
}
|
||||
|
||||
void set_unorm_view(VkImageView view_)
|
||||
{
|
||||
VK_ASSERT(unorm_view == VK_NULL_HANDLE);
|
||||
unorm_view = view_;
|
||||
}
|
||||
|
||||
void set_srgb_view(VkImageView view_)
|
||||
{
|
||||
VK_ASSERT(srgb_view == VK_NULL_HANDLE);
|
||||
srgb_view = view_;
|
||||
}
|
||||
|
||||
// By default, gets a combined view which includes all aspects in the image.
|
||||
// This would be used mostly for render targets.
|
||||
VkImageView get_view() const
|
||||
{
|
||||
return view;
|
||||
}
|
||||
|
||||
VkImageView get_render_target_view(unsigned layer) const;
|
||||
|
||||
// Gets an image view which only includes floating point domains.
|
||||
// Takes effect when we want to sample from an image which is Depth/Stencil,
|
||||
// but we only want to sample depth.
|
||||
VkImageView get_float_view() const
|
||||
{
|
||||
return depth_view != VK_NULL_HANDLE ? depth_view : view;
|
||||
}
|
||||
|
||||
// Gets an image view which only includes integer domains.
|
||||
// Takes effect when we want to sample from an image which is Depth/Stencil,
|
||||
// but we only want to sample stencil.
|
||||
VkImageView get_integer_view() const
|
||||
{
|
||||
return stencil_view != VK_NULL_HANDLE ? stencil_view : view;
|
||||
}
|
||||
|
||||
VkImageView get_unorm_view() const
|
||||
{
|
||||
return unorm_view;
|
||||
}
|
||||
|
||||
VkImageView get_srgb_view() const
|
||||
{
|
||||
return srgb_view;
|
||||
}
|
||||
|
||||
VkFormat get_format() const
|
||||
{
|
||||
return info.format;
|
||||
}
|
||||
|
||||
const Image &get_image() const
|
||||
{
|
||||
return *info.image;
|
||||
}
|
||||
|
||||
const ImageViewCreateInfo &get_create_info() const
|
||||
{
|
||||
return info;
|
||||
}
|
||||
|
||||
unsigned get_view_width() const;
|
||||
unsigned get_view_height() const;
|
||||
unsigned get_view_depth() const;
|
||||
|
||||
private:
|
||||
Device *device;
|
||||
VkImageView view;
|
||||
std::vector<VkImageView> render_target_views;
|
||||
VkImageView depth_view = VK_NULL_HANDLE;
|
||||
VkImageView stencil_view = VK_NULL_HANDLE;
|
||||
VkImageView unorm_view = VK_NULL_HANDLE;
|
||||
VkImageView srgb_view = VK_NULL_HANDLE;
|
||||
ImageViewCreateInfo info;
|
||||
};
|
||||
|
||||
using ImageViewHandle = Util::IntrusivePtr<ImageView>;
|
||||
|
||||
enum class ImageDomain
|
||||
{
|
||||
Physical,
|
||||
Transient,
|
||||
LinearHostCached,
|
||||
LinearHost
|
||||
};
|
||||
|
||||
struct ImageCreateInfo
|
||||
{
|
||||
ImageDomain domain = ImageDomain::Physical;
|
||||
unsigned width = 0;
|
||||
unsigned height = 0;
|
||||
unsigned depth = 1;
|
||||
unsigned levels = 1;
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
VkImageType type = VK_IMAGE_TYPE_2D;
|
||||
unsigned layers = 1;
|
||||
VkImageUsageFlags usage = 0;
|
||||
VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
VkImageCreateFlags flags = 0;
|
||||
ImageMiscFlags misc = 0;
|
||||
VkImageLayout initial_layout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
VkComponentMapping swizzle = {
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
};
|
||||
const DeviceAllocation **memory_aliases = nullptr;
|
||||
unsigned num_memory_aliases = 0;
|
||||
const ImmutableYcbcrConversion *ycbcr_conversion = nullptr;
|
||||
void *pnext = nullptr;
|
||||
ExternalHandle external;
|
||||
|
||||
static ImageCreateInfo immutable_image(const TextureFormatLayout &layout)
|
||||
{
|
||||
Vulkan::ImageCreateInfo info;
|
||||
info.width = layout.get_width();
|
||||
info.height = layout.get_height();
|
||||
info.type = layout.get_image_type();
|
||||
info.depth = layout.get_depth();
|
||||
info.format = layout.get_format();
|
||||
info.layers = layout.get_layers();
|
||||
info.levels = layout.get_levels();
|
||||
info.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
info.initial_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
info.domain = ImageDomain::Physical;
|
||||
return info;
|
||||
}
|
||||
|
||||
static ImageCreateInfo immutable_2d_image(unsigned width, unsigned height, VkFormat format, bool mipmapped = false)
|
||||
{
|
||||
ImageCreateInfo info;
|
||||
info.width = width;
|
||||
info.height = height;
|
||||
info.depth = 1;
|
||||
info.levels = mipmapped ? 0u : 1u;
|
||||
info.format = format;
|
||||
info.type = VK_IMAGE_TYPE_2D;
|
||||
info.layers = 1;
|
||||
info.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
info.flags = 0;
|
||||
info.misc = mipmapped ? unsigned(IMAGE_MISC_GENERATE_MIPS_BIT) : 0u;
|
||||
info.initial_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
return info;
|
||||
}
|
||||
|
||||
static ImageCreateInfo
|
||||
immutable_3d_image(unsigned width, unsigned height, unsigned depth, VkFormat format, bool mipmapped = false)
|
||||
{
|
||||
ImageCreateInfo info = immutable_2d_image(width, height, format, mipmapped);
|
||||
info.depth = depth;
|
||||
info.type = VK_IMAGE_TYPE_3D;
|
||||
return info;
|
||||
}
|
||||
|
||||
static ImageCreateInfo render_target(unsigned width, unsigned height, VkFormat format)
|
||||
{
|
||||
ImageCreateInfo info;
|
||||
info.width = width;
|
||||
info.height = height;
|
||||
info.depth = 1;
|
||||
info.levels = 1;
|
||||
info.format = format;
|
||||
info.type = VK_IMAGE_TYPE_2D;
|
||||
info.layers = 1;
|
||||
info.usage = (format_has_depth_or_stencil_aspect(format) ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT :
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
|
||||
info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
info.flags = 0;
|
||||
info.misc = 0;
|
||||
info.initial_layout = format_has_depth_or_stencil_aspect(format) ?
|
||||
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL :
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
return info;
|
||||
}
|
||||
|
||||
static ImageCreateInfo transient_render_target(unsigned width, unsigned height, VkFormat format)
|
||||
{
|
||||
ImageCreateInfo info;
|
||||
info.domain = ImageDomain::Transient;
|
||||
info.width = width;
|
||||
info.height = height;
|
||||
info.depth = 1;
|
||||
info.levels = 1;
|
||||
info.format = format;
|
||||
info.type = VK_IMAGE_TYPE_2D;
|
||||
info.layers = 1;
|
||||
info.usage = (format_has_depth_or_stencil_aspect(format) ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT :
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) |
|
||||
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
|
||||
info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
info.flags = 0;
|
||||
info.misc = 0;
|
||||
info.initial_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
return info;
|
||||
}
|
||||
|
||||
static uint32_t compute_view_formats(const ImageCreateInfo &info, VkFormat *formats)
|
||||
{
|
||||
if ((info.misc & IMAGE_MISC_MUTABLE_SRGB_BIT) == 0)
|
||||
return 0;
|
||||
|
||||
switch (info.format)
|
||||
{
|
||||
case VK_FORMAT_R8G8B8A8_UNORM:
|
||||
case VK_FORMAT_R8G8B8A8_SRGB:
|
||||
formats[0] = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
formats[1] = VK_FORMAT_R8G8B8A8_SRGB;
|
||||
return 2;
|
||||
|
||||
case VK_FORMAT_B8G8R8A8_UNORM:
|
||||
case VK_FORMAT_B8G8R8A8_SRGB:
|
||||
formats[0] = VK_FORMAT_B8G8R8A8_UNORM;
|
||||
formats[1] = VK_FORMAT_B8G8R8A8_SRGB;
|
||||
return 2;
|
||||
|
||||
case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
|
||||
case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
|
||||
formats[0] = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
formats[1] = VK_FORMAT_A8B8G8R8_SRGB_PACK32;
|
||||
return 2;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Image;
|
||||
|
||||
struct ImageDeleter
|
||||
{
|
||||
void operator()(Image *image);
|
||||
};
|
||||
|
||||
enum class Layout
|
||||
{
|
||||
Optimal,
|
||||
General
|
||||
};
|
||||
|
||||
class Image : public Util::IntrusivePtrEnabled<Image, ImageDeleter, HandleCounter>,
|
||||
public Cookie, public InternalSyncEnabled
|
||||
{
|
||||
public:
|
||||
friend struct ImageDeleter;
|
||||
|
||||
~Image();
|
||||
|
||||
Image(Image &&) = delete;
|
||||
|
||||
Image &operator=(Image &&) = delete;
|
||||
|
||||
const ImageView &get_view() const
|
||||
{
|
||||
VK_ASSERT(view);
|
||||
return *view;
|
||||
}
|
||||
|
||||
ImageView &get_view()
|
||||
{
|
||||
VK_ASSERT(view);
|
||||
return *view;
|
||||
}
|
||||
|
||||
VkImage get_image() const
|
||||
{
|
||||
return image;
|
||||
}
|
||||
|
||||
VkFormat get_format() const
|
||||
{
|
||||
return create_info.format;
|
||||
}
|
||||
|
||||
uint32_t get_width(uint32_t lod = 0) const
|
||||
{
|
||||
return std::max<uint32_t>(1u, create_info.width >> lod);
|
||||
}
|
||||
|
||||
uint32_t get_height(uint32_t lod = 0) const
|
||||
{
|
||||
return std::max<uint32_t>(1u, create_info.height >> lod);
|
||||
}
|
||||
|
||||
uint32_t get_depth(uint32_t lod = 0) const
|
||||
{
|
||||
return std::max<uint32_t>(1u, create_info.depth >> lod);
|
||||
}
|
||||
|
||||
const ImageCreateInfo &get_create_info() const
|
||||
{
|
||||
return create_info;
|
||||
}
|
||||
|
||||
VkImageLayout get_layout(VkImageLayout optimal) const
|
||||
{
|
||||
return layout_type == Layout::Optimal ? optimal : VK_IMAGE_LAYOUT_GENERAL;
|
||||
}
|
||||
|
||||
Layout get_layout_type() const
|
||||
{
|
||||
return layout_type;
|
||||
}
|
||||
|
||||
void set_layout(Layout layout)
|
||||
{
|
||||
layout_type = layout;
|
||||
}
|
||||
|
||||
bool is_swapchain_image() const
|
||||
{
|
||||
return swapchain_layout != VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
}
|
||||
|
||||
VkImageLayout get_swapchain_layout() const
|
||||
{
|
||||
return swapchain_layout;
|
||||
}
|
||||
|
||||
void set_swapchain_layout(VkImageLayout layout)
|
||||
{
|
||||
swapchain_layout = layout;
|
||||
}
|
||||
|
||||
const DeviceAllocation &get_allocation() const
|
||||
{
|
||||
return alloc;
|
||||
}
|
||||
|
||||
void disown_image();
|
||||
void disown_memory_allocation();
|
||||
DeviceAllocation take_allocation_ownership();
|
||||
|
||||
void set_surface_transform(VkSurfaceTransformFlagBitsKHR transform)
|
||||
{
|
||||
surface_transform = transform;
|
||||
if (transform != VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
|
||||
{
|
||||
const VkImageUsageFlags safe_usage_flags =
|
||||
VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT |
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
||||
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
|
||||
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
|
||||
|
||||
if ((create_info.usage & ~safe_usage_flags) != 0)
|
||||
{
|
||||
LOGW("Using surface transform for non-pure render target image (usage: %u). This can lead to weird results.\n",
|
||||
create_info.usage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VkSurfaceTransformFlagBitsKHR get_surface_transform() const
|
||||
{
|
||||
return surface_transform;
|
||||
}
|
||||
|
||||
ExternalHandle export_handle();
|
||||
|
||||
private:
|
||||
friend class Util::ObjectPool<Image>;
|
||||
|
||||
Image(Device *device, VkImage image, VkImageView default_view, const DeviceAllocation &alloc,
|
||||
const ImageCreateInfo &info, VkImageViewType view_type);
|
||||
|
||||
Device *device;
|
||||
VkImage image;
|
||||
ImageViewHandle view;
|
||||
DeviceAllocation alloc;
|
||||
ImageCreateInfo create_info;
|
||||
|
||||
Layout layout_type = Layout::Optimal;
|
||||
VkImageLayout swapchain_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
VkSurfaceTransformFlagBitsKHR surface_transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
bool owns_image = true;
|
||||
bool owns_memory_allocation = true;
|
||||
};
|
||||
|
||||
using ImageHandle = Util::IntrusivePtr<Image>;
|
||||
|
||||
class LinearHostImage;
|
||||
struct LinearHostImageDeleter
|
||||
{
|
||||
void operator()(LinearHostImage *image);
|
||||
};
|
||||
|
||||
class Buffer;
|
||||
|
||||
enum LinearHostImageCreateInfoFlagBits
|
||||
{
|
||||
LINEAR_HOST_IMAGE_HOST_CACHED_BIT = 1 << 0,
|
||||
LINEAR_HOST_IMAGE_REQUIRE_LINEAR_FILTER_BIT = 1 << 1,
|
||||
LINEAR_HOST_IMAGE_IGNORE_DEVICE_LOCAL_BIT = 1 << 2
|
||||
};
|
||||
using LinearHostImageCreateInfoFlags = uint32_t;
|
||||
|
||||
struct LinearHostImageCreateInfo
|
||||
{
|
||||
unsigned width = 0;
|
||||
unsigned height = 0;
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
VkImageUsageFlags usage = 0;
|
||||
VkPipelineStageFlags2 stages = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
LinearHostImageCreateInfoFlags flags = 0;
|
||||
};
|
||||
|
||||
// Special image type which supports direct CPU mapping.
|
||||
// Useful optimization for UMA implementations of Vulkan where we don't necessarily need
|
||||
// to perform staging copies. It gracefully falls back to staging buffer as needed.
|
||||
// Only usage flag SAMPLED_BIT is currently supported.
|
||||
class LinearHostImage : public Util::IntrusivePtrEnabled<LinearHostImage, LinearHostImageDeleter, HandleCounter>
|
||||
{
|
||||
public:
|
||||
friend struct LinearHostImageDeleter;
|
||||
|
||||
size_t get_row_pitch_bytes() const;
|
||||
size_t get_offset() const;
|
||||
const ImageView &get_view() const;
|
||||
const Image &get_image() const;
|
||||
const DeviceAllocation &get_host_visible_allocation() const;
|
||||
const Buffer &get_host_visible_buffer() const;
|
||||
bool need_staging_copy() const;
|
||||
VkPipelineStageFlags2 get_used_pipeline_stages() const;
|
||||
|
||||
private:
|
||||
friend class Util::ObjectPool<LinearHostImage>;
|
||||
LinearHostImage(Device *device, ImageHandle gpu_image, Util::IntrusivePtr<Buffer> cpu_image,
|
||||
VkPipelineStageFlags2 stages);
|
||||
Device *device;
|
||||
ImageHandle gpu_image;
|
||||
Util::IntrusivePtr<Buffer> cpu_image;
|
||||
VkPipelineStageFlags2 stages;
|
||||
size_t row_pitch;
|
||||
size_t row_offset;
|
||||
};
|
||||
using LinearHostImageHandle = Util::IntrusivePtr<LinearHostImage>;
|
||||
}
|
||||
108
external/parallel-rdp/parallel-rdp-standalone/vulkan/indirect_layout.cpp
vendored
Normal file
108
external/parallel-rdp/parallel-rdp-standalone/vulkan/indirect_layout.cpp
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "indirect_layout.hpp"
|
||||
#include "device.hpp"
|
||||
#include "small_vector.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
IndirectLayout::IndirectLayout(Device *device_, const IndirectLayoutToken *tokens, uint32_t num_tokens, uint32_t stride)
|
||||
: device(device_)
|
||||
{
|
||||
VkIndirectCommandsLayoutCreateInfoNV info = { VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NV };
|
||||
info.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
info.pStreamStrides = &stride;
|
||||
info.streamCount = 1;
|
||||
|
||||
Util::SmallVector<VkIndirectCommandsLayoutTokenNV, 8> nv_tokens;
|
||||
nv_tokens.reserve(num_tokens);
|
||||
|
||||
for (uint32_t i = 0; i < num_tokens; i++)
|
||||
{
|
||||
VkIndirectCommandsLayoutTokenNV token = { VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_TOKEN_NV };
|
||||
switch (tokens[i].type)
|
||||
{
|
||||
case IndirectLayoutToken::Type::VBO:
|
||||
token.tokenType = VK_INDIRECT_COMMANDS_TOKEN_TYPE_VERTEX_BUFFER_NV;
|
||||
token.vertexBindingUnit = tokens[i].data.vbo.binding;
|
||||
break;
|
||||
|
||||
case IndirectLayoutToken::Type::IBO:
|
||||
token.tokenType = VK_INDIRECT_COMMANDS_TOKEN_TYPE_INDEX_BUFFER_NV;
|
||||
break;
|
||||
|
||||
case IndirectLayoutToken::Type::PushConstant:
|
||||
token.tokenType = VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_NV;
|
||||
token.pushconstantSize = tokens[i].data.push.range;
|
||||
token.pushconstantOffset = tokens[i].data.push.offset;
|
||||
token.pushconstantPipelineLayout = tokens[i].data.push.layout->get_layout();
|
||||
token.pushconstantShaderStageFlags = tokens[i].data.push.layout->get_resource_layout().push_constant_range.stageFlags;
|
||||
break;
|
||||
|
||||
case IndirectLayoutToken::Type::Draw:
|
||||
token.tokenType = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_NV;
|
||||
break;
|
||||
|
||||
case IndirectLayoutToken::Type::DrawIndexed:
|
||||
token.tokenType = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_INDEXED_NV;
|
||||
break;
|
||||
|
||||
case IndirectLayoutToken::Type::Shader:
|
||||
token.tokenType = VK_INDIRECT_COMMANDS_TOKEN_TYPE_SHADER_GROUP_NV;
|
||||
break;
|
||||
|
||||
case IndirectLayoutToken::Type::MeshTasks:
|
||||
token.tokenType = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_MESH_TASKS_NV;
|
||||
break;
|
||||
|
||||
case IndirectLayoutToken::Type::Dispatch:
|
||||
token.tokenType = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_NV;
|
||||
info.pipelineBindPoint = VK_PIPELINE_BIND_POINT_COMPUTE;
|
||||
break;
|
||||
|
||||
default:
|
||||
LOGE("Invalid token type.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
token.offset = tokens[i].offset;
|
||||
|
||||
nv_tokens.push_back(token);
|
||||
}
|
||||
|
||||
info.pTokens = nv_tokens.data();
|
||||
info.tokenCount = num_tokens;
|
||||
bind_point = info.pipelineBindPoint;
|
||||
|
||||
auto &table = device->get_device_table();
|
||||
if (table.vkCreateIndirectCommandsLayoutNV(device->get_device(), &info, nullptr, &layout) != VK_SUCCESS)
|
||||
{
|
||||
LOGE("Failed to create indirect layout.\n");
|
||||
}
|
||||
}
|
||||
|
||||
IndirectLayout::~IndirectLayout()
|
||||
{
|
||||
device->get_device_table().vkDestroyIndirectCommandsLayoutNV(device->get_device(), layout, nullptr);
|
||||
}
|
||||
}
|
||||
91
external/parallel-rdp/parallel-rdp-standalone/vulkan/indirect_layout.hpp
vendored
Normal file
91
external/parallel-rdp/parallel-rdp-standalone/vulkan/indirect_layout.hpp
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vulkan_headers.hpp"
|
||||
#include "vulkan_common.hpp"
|
||||
#include "cookie.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class Device;
|
||||
class PipelineLayout;
|
||||
|
||||
struct IndirectLayoutToken
|
||||
{
|
||||
enum class Type
|
||||
{
|
||||
Invalid = 0,
|
||||
Shader,
|
||||
PushConstant,
|
||||
VBO,
|
||||
IBO,
|
||||
Draw,
|
||||
DrawIndexed,
|
||||
MeshTasks,
|
||||
Dispatch
|
||||
};
|
||||
|
||||
Type type = Type::Invalid;
|
||||
uint32_t offset = 0;
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint32_t offset;
|
||||
uint32_t range;
|
||||
const PipelineLayout *layout;
|
||||
} push;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t binding;
|
||||
} vbo;
|
||||
} data = {};
|
||||
};
|
||||
|
||||
class IndirectLayout : public HashedObject<IndirectLayout>
|
||||
{
|
||||
public:
|
||||
IndirectLayout(Device *device, const IndirectLayoutToken *token, uint32_t num_tokens, uint32_t stride);
|
||||
~IndirectLayout();
|
||||
|
||||
VkIndirectCommandsLayoutNV get_layout() const
|
||||
{
|
||||
return layout;
|
||||
}
|
||||
|
||||
VkPipelineBindPoint get_bind_point() const
|
||||
{
|
||||
return bind_point;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Device;
|
||||
|
||||
Device *device;
|
||||
VkIndirectCommandsLayoutNV layout;
|
||||
VkPipelineBindPoint bind_point;
|
||||
};
|
||||
}
|
||||
40
external/parallel-rdp/parallel-rdp-standalone/vulkan/limits.hpp
vendored
Normal file
40
external/parallel-rdp/parallel-rdp-standalone/vulkan/limits.hpp
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
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;
|
||||
constexpr unsigned VULKAN_NUM_VERTEX_ATTRIBS = 16;
|
||||
constexpr unsigned VULKAN_NUM_VERTEX_BUFFERS = 4;
|
||||
constexpr unsigned VULKAN_PUSH_CONSTANT_SIZE = 128;
|
||||
constexpr unsigned VULKAN_MAX_UBO_SIZE = 64 * 1024;
|
||||
constexpr unsigned VULKAN_NUM_USER_SPEC_CONSTANTS = 8;
|
||||
constexpr unsigned VULKAN_NUM_INTERNAL_SPEC_CONSTANTS = 4;
|
||||
constexpr unsigned VULKAN_NUM_TOTAL_SPEC_CONSTANTS =
|
||||
VULKAN_NUM_USER_SPEC_CONSTANTS + VULKAN_NUM_INTERNAL_SPEC_CONSTANTS;
|
||||
}
|
||||
825
external/parallel-rdp/parallel-rdp-standalone/vulkan/memory_allocator.cpp
vendored
Normal file
825
external/parallel-rdp/parallel-rdp-standalone/vulkan/memory_allocator.cpp
vendored
Normal file
@@ -0,0 +1,825 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "memory_allocator.hpp"
|
||||
#include "timeline_trace_file.hpp"
|
||||
#include "device.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
static bool allocation_mode_supports_bda(AllocationMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case AllocationMode::LinearDevice:
|
||||
case AllocationMode::LinearHostMappable:
|
||||
case AllocationMode::LinearDeviceHighPriority:
|
||||
return true;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DeviceAllocation::free_immediate()
|
||||
{
|
||||
if (!alloc)
|
||||
return;
|
||||
|
||||
alloc->free(heap, mask);
|
||||
alloc = nullptr;
|
||||
base = VK_NULL_HANDLE;
|
||||
mask = 0;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
ExternalHandle DeviceAllocation::export_handle(Device &device)
|
||||
{
|
||||
ExternalHandle h;
|
||||
|
||||
if (exportable_types == 0)
|
||||
{
|
||||
LOGE("Cannot export from this allocation.\n");
|
||||
return h;
|
||||
}
|
||||
|
||||
auto &table = device.get_device_table();
|
||||
|
||||
#ifdef _WIN32
|
||||
VkMemoryGetWin32HandleInfoKHR handle_info = { VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR };
|
||||
handle_info.handleType = static_cast<VkExternalMemoryHandleTypeFlagBits>(exportable_types);
|
||||
handle_info.memory = base;
|
||||
h.memory_handle_type = handle_info.handleType;
|
||||
|
||||
if (table.vkGetMemoryWin32HandleKHR(device.get_device(), &handle_info, &h.handle) != VK_SUCCESS)
|
||||
{
|
||||
LOGE("Failed to export memory handle.\n");
|
||||
h.handle = nullptr;
|
||||
}
|
||||
#else
|
||||
VkMemoryGetFdInfoKHR fd_info = { VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR };
|
||||
fd_info.handleType = static_cast<VkExternalMemoryHandleTypeFlagBits>(exportable_types);
|
||||
fd_info.memory = base;
|
||||
h.memory_handle_type = fd_info.handleType;
|
||||
|
||||
if (table.vkGetMemoryFdKHR(device.get_device(), &fd_info, &h.handle) != VK_SUCCESS)
|
||||
{
|
||||
LOGE("Failed to export memory handle.\n");
|
||||
h.handle = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
void DeviceAllocation::free_immediate(DeviceAllocator &allocator)
|
||||
{
|
||||
if (alloc)
|
||||
free_immediate();
|
||||
else if (base)
|
||||
{
|
||||
allocator.internal_free_no_recycle(size, memory_type, base);
|
||||
base = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceAllocation::free_global(DeviceAllocator &allocator, uint32_t size_, uint32_t memory_type_)
|
||||
{
|
||||
if (base)
|
||||
{
|
||||
allocator.internal_free(size_, memory_type_, mode, base, host_base != nullptr);
|
||||
base = VK_NULL_HANDLE;
|
||||
mask = 0;
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ClassAllocator::prepare_allocation(DeviceAllocation *alloc, Util::IntrusiveList<MiniHeap>::Iterator heap_itr,
|
||||
const Util::SuballocationResult &suballoc)
|
||||
{
|
||||
auto &heap = *heap_itr;
|
||||
alloc->heap = heap_itr;
|
||||
alloc->base = heap.allocation.base;
|
||||
alloc->offset = suballoc.offset + heap.allocation.offset;
|
||||
alloc->mask = suballoc.mask;
|
||||
alloc->size = suballoc.size;
|
||||
|
||||
if (heap.allocation.host_base)
|
||||
alloc->host_base = heap.allocation.host_base + suballoc.offset;
|
||||
|
||||
VK_ASSERT(heap.allocation.mode == global_allocator_mode);
|
||||
VK_ASSERT(heap.allocation.memory_type == memory_type);
|
||||
|
||||
alloc->mode = global_allocator_mode;
|
||||
alloc->memory_type = memory_type;
|
||||
alloc->alloc = this;
|
||||
}
|
||||
|
||||
static inline bool mode_request_host_mapping(AllocationMode mode)
|
||||
{
|
||||
// LinearHostMapping will always work. LinearDevice ones will speculatively work on UMA.
|
||||
return mode == AllocationMode::LinearHostMappable ||
|
||||
mode == AllocationMode::LinearDevice ||
|
||||
mode == AllocationMode::LinearDeviceHighPriority;
|
||||
}
|
||||
|
||||
bool ClassAllocator::allocate_backing_heap(DeviceAllocation *alloc)
|
||||
{
|
||||
uint32_t alloc_size = sub_block_size * Util::LegionAllocator::NumSubBlocks;
|
||||
|
||||
if (parent)
|
||||
{
|
||||
return parent->allocate(alloc_size, alloc);
|
||||
}
|
||||
else
|
||||
{
|
||||
alloc->offset = 0;
|
||||
alloc->host_base = nullptr;
|
||||
alloc->mode = global_allocator_mode;
|
||||
alloc->memory_type = memory_type;
|
||||
|
||||
return global_allocator->internal_allocate(
|
||||
alloc_size, memory_type, global_allocator_mode, &alloc->base,
|
||||
mode_request_host_mapping(global_allocator_mode) ? &alloc->host_base : nullptr,
|
||||
VK_OBJECT_TYPE_DEVICE, 0, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void ClassAllocator::free_backing_heap(DeviceAllocation *allocation)
|
||||
{
|
||||
assert(allocation->mode == global_allocator_mode);
|
||||
assert(allocation->memory_type == memory_type);
|
||||
|
||||
// Our mini-heap is completely freed, free to higher level allocator.
|
||||
if (parent)
|
||||
allocation->free_immediate();
|
||||
else
|
||||
allocation->free_global(*global_allocator, sub_block_size * Util::LegionAllocator::NumSubBlocks, memory_type);
|
||||
}
|
||||
|
||||
bool Allocator::allocate_global(uint32_t size, AllocationMode mode, DeviceAllocation *alloc)
|
||||
{
|
||||
// Fall back to global allocation, do not recycle.
|
||||
alloc->host_base = nullptr;
|
||||
if (!global_allocator->internal_allocate(
|
||||
size, memory_type, mode, &alloc->base,
|
||||
mode_request_host_mapping(mode) ? &alloc->host_base : nullptr,
|
||||
VK_OBJECT_TYPE_DEVICE, 0, nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
alloc->mode = mode;
|
||||
alloc->alloc = nullptr;
|
||||
alloc->memory_type = memory_type;
|
||||
alloc->size = size;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Allocator::allocate_dedicated(uint32_t size, AllocationMode mode, DeviceAllocation *alloc,
|
||||
VkObjectType type, uint64_t object, ExternalHandle *external)
|
||||
{
|
||||
// Fall back to global allocation, do not recycle.
|
||||
alloc->host_base = nullptr;
|
||||
if (!global_allocator->internal_allocate(
|
||||
size, memory_type, mode, &alloc->base,
|
||||
mode_request_host_mapping(mode) ? &alloc->host_base : nullptr,
|
||||
type, object, external))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
alloc->mode = mode;
|
||||
alloc->alloc = nullptr;
|
||||
alloc->memory_type = memory_type;
|
||||
alloc->size = size;
|
||||
|
||||
// If we imported memory instead, do not allow handle export.
|
||||
if (external && !(*external))
|
||||
alloc->exportable_types = external->memory_handle_type;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DeviceAllocation DeviceAllocation::make_imported_allocation(VkDeviceMemory memory, VkDeviceSize size,
|
||||
uint32_t memory_type)
|
||||
{
|
||||
DeviceAllocation alloc = {};
|
||||
alloc.base = memory;
|
||||
alloc.offset = 0;
|
||||
alloc.size = size;
|
||||
alloc.memory_type = memory_type;
|
||||
return alloc;
|
||||
}
|
||||
|
||||
bool Allocator::allocate(uint32_t size, uint32_t alignment, AllocationMode mode, DeviceAllocation *alloc)
|
||||
{
|
||||
for (auto &c : classes)
|
||||
{
|
||||
auto &suballocator = c[unsigned(mode)];
|
||||
|
||||
// Find a suitable class to allocate from.
|
||||
if (size <= suballocator.get_max_allocation_size())
|
||||
{
|
||||
if (alignment > suballocator.get_block_alignment())
|
||||
{
|
||||
size_t padded_size = size + (alignment - suballocator.get_block_alignment());
|
||||
if (padded_size <= suballocator.get_max_allocation_size())
|
||||
size = padded_size;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
bool ret = suballocator.allocate(size, alloc);
|
||||
if (ret)
|
||||
{
|
||||
uint32_t aligned_offset = (alloc->offset + alignment - 1) & ~(alignment - 1);
|
||||
if (alloc->host_base)
|
||||
alloc->host_base += aligned_offset - alloc->offset;
|
||||
alloc->offset = aligned_offset;
|
||||
VK_ASSERT(alloc->mode == mode);
|
||||
VK_ASSERT(alloc->memory_type == memory_type);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (!allocate_global(size, mode, alloc))
|
||||
return false;
|
||||
|
||||
VK_ASSERT(alloc->mode == mode);
|
||||
VK_ASSERT(alloc->memory_type == memory_type);
|
||||
return true;
|
||||
}
|
||||
|
||||
Allocator::Allocator(Util::ObjectPool<MiniHeap> &object_pool)
|
||||
{
|
||||
for (int i = 0; i < Util::ecast(MemoryClass::Count) - 1; i++)
|
||||
for (int j = 0; j < Util::ecast(AllocationMode::Count); j++)
|
||||
classes[i][j].set_parent(&classes[i + 1][j]);
|
||||
|
||||
for (auto &c : classes)
|
||||
for (auto &m : c)
|
||||
m.set_object_pool(&object_pool);
|
||||
|
||||
for (int j = 0; j < Util::ecast(AllocationMode::Count); j++)
|
||||
{
|
||||
auto mode = static_cast<AllocationMode>(j);
|
||||
|
||||
// 128 chunk
|
||||
get_class_allocator(MemoryClass::Small, mode).set_sub_block_size(128);
|
||||
// 4k chunk
|
||||
get_class_allocator(MemoryClass::Medium, mode).set_sub_block_size(
|
||||
128 * Util::LegionAllocator::NumSubBlocks); // 4K
|
||||
// 128k chunk
|
||||
get_class_allocator(MemoryClass::Large, mode).set_sub_block_size(
|
||||
128 * Util::LegionAllocator::NumSubBlocks *
|
||||
Util::LegionAllocator::NumSubBlocks);
|
||||
// 2M chunk
|
||||
get_class_allocator(MemoryClass::Huge, mode).set_sub_block_size(
|
||||
64 * Util::LegionAllocator::NumSubBlocks * Util::LegionAllocator::NumSubBlocks *
|
||||
Util::LegionAllocator::NumSubBlocks);
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceAllocator::init(Device *device_)
|
||||
{
|
||||
device = device_;
|
||||
table = &device->get_device_table();
|
||||
mem_props = device->get_memory_properties();
|
||||
const auto &props = device->get_gpu_properties();
|
||||
atom_alignment = props.limits.nonCoherentAtomSize;
|
||||
|
||||
heaps.clear();
|
||||
allocators.clear();
|
||||
|
||||
heaps.resize(mem_props.memoryHeapCount);
|
||||
allocators.reserve(mem_props.memoryTypeCount);
|
||||
for (unsigned i = 0; i < mem_props.memoryTypeCount; i++)
|
||||
{
|
||||
allocators.emplace_back(new Allocator(object_pool));
|
||||
allocators.back()->set_global_allocator(this, i);
|
||||
}
|
||||
|
||||
HeapBudget budgets[VK_MAX_MEMORY_HEAPS];
|
||||
get_memory_budget(budgets);
|
||||
|
||||
// Figure out if we have a PCI-e BAR heap.
|
||||
// We need to be very careful with our budget (usually 128 MiB out of 256 MiB) on these heaps
|
||||
// since they can lead to instability if overused.
|
||||
VkMemoryPropertyFlags combined_allowed_flags[VK_MAX_MEMORY_HEAPS] = {};
|
||||
for (uint32_t i = 0; i < mem_props.memoryTypeCount; i++)
|
||||
{
|
||||
uint32_t heap_index = mem_props.memoryTypes[i].heapIndex;
|
||||
combined_allowed_flags[heap_index] |= mem_props.memoryTypes[i].propertyFlags;
|
||||
}
|
||||
|
||||
bool has_host_only_heap = false;
|
||||
bool has_device_only_heap = false;
|
||||
VkDeviceSize host_heap_size = 0;
|
||||
VkDeviceSize device_heap_size = 0;
|
||||
const VkMemoryPropertyFlags pinned_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
|
||||
for (uint32_t i = 0; i < mem_props.memoryHeapCount; i++)
|
||||
{
|
||||
if ((combined_allowed_flags[i] & pinned_flags) == VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
|
||||
{
|
||||
has_host_only_heap = true;
|
||||
host_heap_size = (std::max)(host_heap_size, mem_props.memoryHeaps[i].size);
|
||||
}
|
||||
else if ((combined_allowed_flags[i] & pinned_flags) == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
|
||||
{
|
||||
has_device_only_heap = true;
|
||||
device_heap_size = (std::max)(device_heap_size, mem_props.memoryHeaps[i].size);
|
||||
}
|
||||
}
|
||||
|
||||
// If we have ReBAR enabled, we generally won't find DEVICE only and HOST only heaps.
|
||||
// Budget criticalness should only be considered if we have the default small BAR heap (256 MiB).
|
||||
if (has_host_only_heap && has_device_only_heap)
|
||||
{
|
||||
for (uint32_t i = 0; i < mem_props.memoryHeapCount; i++)
|
||||
{
|
||||
if ((combined_allowed_flags[i] & pinned_flags) == pinned_flags &&
|
||||
mem_props.memoryHeaps[i].size < host_heap_size &&
|
||||
mem_props.memoryHeaps[i].size < device_heap_size)
|
||||
{
|
||||
memory_heap_is_budget_critical[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DeviceAllocator::allocate_generic_memory(uint32_t size, uint32_t alignment, AllocationMode mode,
|
||||
uint32_t memory_type, DeviceAllocation *alloc)
|
||||
{
|
||||
return allocators[memory_type]->allocate(size, alignment, mode, alloc);
|
||||
}
|
||||
|
||||
bool DeviceAllocator::allocate_buffer_memory(uint32_t size, uint32_t alignment, AllocationMode mode,
|
||||
uint32_t memory_type, VkBuffer buffer,
|
||||
DeviceAllocation *alloc, ExternalHandle *external)
|
||||
{
|
||||
if (mode == AllocationMode::External)
|
||||
{
|
||||
return allocators[memory_type]->allocate_dedicated(
|
||||
size, mode, alloc,
|
||||
VK_OBJECT_TYPE_BUFFER, (uint64_t)buffer, external);
|
||||
}
|
||||
else
|
||||
{
|
||||
return allocate_generic_memory(size, alignment, mode, memory_type, alloc);
|
||||
}
|
||||
}
|
||||
|
||||
bool DeviceAllocator::allocate_image_memory(uint32_t size, uint32_t alignment, AllocationMode mode, uint32_t memory_type,
|
||||
VkImage image, bool force_no_dedicated, DeviceAllocation *alloc,
|
||||
ExternalHandle *external)
|
||||
{
|
||||
if (force_no_dedicated)
|
||||
{
|
||||
VK_ASSERT(mode != AllocationMode::External && !external);
|
||||
return allocate_generic_memory(size, alignment, mode, memory_type, alloc);
|
||||
}
|
||||
|
||||
VkImageMemoryRequirementsInfo2 info = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2 };
|
||||
info.image = image;
|
||||
|
||||
VkMemoryDedicatedRequirements dedicated_req = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS };
|
||||
VkMemoryRequirements2 mem_req = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2 };
|
||||
mem_req.pNext = &dedicated_req;
|
||||
table->vkGetImageMemoryRequirements2(device->get_device(), &info, &mem_req);
|
||||
|
||||
if (dedicated_req.prefersDedicatedAllocation ||
|
||||
dedicated_req.requiresDedicatedAllocation ||
|
||||
mode == AllocationMode::External)
|
||||
{
|
||||
return allocators[memory_type]->allocate_dedicated(
|
||||
size, mode, alloc, VK_OBJECT_TYPE_IMAGE, (uint64_t)image, external);
|
||||
}
|
||||
else
|
||||
{
|
||||
return allocate_generic_memory(size, alignment, mode, memory_type, alloc);
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceAllocator::Heap::garbage_collect(Device *device_)
|
||||
{
|
||||
auto &table_ = device_->get_device_table();
|
||||
for (auto &block : blocks)
|
||||
{
|
||||
table_.vkFreeMemory(device_->get_device(), block.memory, nullptr);
|
||||
size -= block.size;
|
||||
}
|
||||
blocks.clear();
|
||||
}
|
||||
|
||||
DeviceAllocator::~DeviceAllocator()
|
||||
{
|
||||
for (auto &heap : heaps)
|
||||
heap.garbage_collect(device);
|
||||
}
|
||||
|
||||
void DeviceAllocator::internal_free(uint32_t size, uint32_t memory_type, AllocationMode mode, VkDeviceMemory memory, bool is_mapped)
|
||||
{
|
||||
if (is_mapped)
|
||||
table->vkUnmapMemory(device->get_device(), memory);
|
||||
|
||||
auto &heap = heaps[mem_props.memoryTypes[memory_type].heapIndex];
|
||||
|
||||
VK_ASSERT(mode != AllocationMode::Count);
|
||||
|
||||
heap.blocks.push_back({ memory, size, memory_type, mode });
|
||||
if (memory_heap_is_budget_critical[mem_props.memoryTypes[memory_type].heapIndex])
|
||||
heap.garbage_collect(device);
|
||||
}
|
||||
|
||||
void DeviceAllocator::internal_free_no_recycle(uint32_t size, uint32_t memory_type, VkDeviceMemory memory)
|
||||
{
|
||||
auto &heap = heaps[mem_props.memoryTypes[memory_type].heapIndex];
|
||||
table->vkFreeMemory(device->get_device(), memory, nullptr);
|
||||
heap.size -= size;
|
||||
}
|
||||
|
||||
void DeviceAllocator::garbage_collect()
|
||||
{
|
||||
for (auto &heap : heaps)
|
||||
heap.garbage_collect(device);
|
||||
}
|
||||
|
||||
void *DeviceAllocator::map_memory(const DeviceAllocation &alloc, MemoryAccessFlags flags,
|
||||
VkDeviceSize offset, VkDeviceSize length)
|
||||
{
|
||||
VkDeviceSize base_offset = offset;
|
||||
|
||||
// This will only happen if the memory type is device local only, which we cannot possibly map.
|
||||
if (!alloc.host_base)
|
||||
return nullptr;
|
||||
|
||||
if ((flags & MEMORY_ACCESS_READ_BIT) &&
|
||||
!(mem_props.memoryTypes[alloc.memory_type].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
|
||||
{
|
||||
offset += alloc.offset;
|
||||
VkDeviceSize end_offset = offset + length;
|
||||
offset &= ~(atom_alignment - 1);
|
||||
length = end_offset - offset;
|
||||
VkDeviceSize size = (length + atom_alignment - 1) & ~(atom_alignment - 1);
|
||||
|
||||
// Have to invalidate cache here.
|
||||
const VkMappedMemoryRange range = {
|
||||
VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, alloc.base, offset, size,
|
||||
};
|
||||
table->vkInvalidateMappedMemoryRanges(device->get_device(), 1, &range);
|
||||
}
|
||||
|
||||
return alloc.host_base + base_offset;
|
||||
}
|
||||
|
||||
void DeviceAllocator::unmap_memory(const DeviceAllocation &alloc, MemoryAccessFlags flags,
|
||||
VkDeviceSize offset, VkDeviceSize length)
|
||||
{
|
||||
// This will only happen if the memory type is device local only, which we cannot possibly map.
|
||||
if (!alloc.host_base)
|
||||
return;
|
||||
|
||||
if ((flags & MEMORY_ACCESS_WRITE_BIT) &&
|
||||
!(mem_props.memoryTypes[alloc.memory_type].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
|
||||
{
|
||||
offset += alloc.offset;
|
||||
VkDeviceSize end_offset = offset + length;
|
||||
offset &= ~(atom_alignment - 1);
|
||||
length = end_offset - offset;
|
||||
VkDeviceSize size = (length + atom_alignment - 1) & ~(atom_alignment - 1);
|
||||
|
||||
// Have to flush caches here.
|
||||
const VkMappedMemoryRange range = {
|
||||
VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, alloc.base, offset, size,
|
||||
};
|
||||
table->vkFlushMappedMemoryRanges(device->get_device(), 1, &range);
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceAllocator::get_memory_budget_nolock(HeapBudget *heap_budgets)
|
||||
{
|
||||
uint32_t num_heaps = mem_props.memoryHeapCount;
|
||||
|
||||
if (device->get_device_features().supports_memory_budget)
|
||||
{
|
||||
VkPhysicalDeviceMemoryProperties2 props =
|
||||
{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2 };
|
||||
VkPhysicalDeviceMemoryBudgetPropertiesEXT budget_props =
|
||||
{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };
|
||||
|
||||
if (device->get_device_features().supports_memory_budget)
|
||||
props.pNext = &budget_props;
|
||||
|
||||
vkGetPhysicalDeviceMemoryProperties2(device->get_physical_device(), &props);
|
||||
|
||||
for (uint32_t i = 0; i < num_heaps; i++)
|
||||
{
|
||||
auto &heap = heap_budgets[i];
|
||||
heap.max_size = mem_props.memoryHeaps[i].size;
|
||||
heap.budget_size = budget_props.heapBudget[i];
|
||||
heap.device_usage = budget_props.heapUsage[i];
|
||||
heap.tracked_usage = heaps[i].size;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint32_t i = 0; i < num_heaps; i++)
|
||||
{
|
||||
auto &heap = heap_budgets[i];
|
||||
heap.max_size = mem_props.memoryHeaps[i].size;
|
||||
// Allow 75%.
|
||||
heap.budget_size = heap.max_size - (heap.max_size / 4);
|
||||
heap.tracked_usage = heaps[i].size;
|
||||
heap.device_usage = heaps[i].size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceAllocator::get_memory_budget(HeapBudget *heap_budgets)
|
||||
{
|
||||
get_memory_budget_nolock(heap_budgets);
|
||||
}
|
||||
|
||||
bool DeviceAllocator::internal_allocate(
|
||||
uint32_t size, uint32_t memory_type, AllocationMode mode,
|
||||
VkDeviceMemory *memory, uint8_t **host_memory,
|
||||
VkObjectType object_type, uint64_t dedicated_object, ExternalHandle *external)
|
||||
{
|
||||
uint32_t heap_index = mem_props.memoryTypes[memory_type].heapIndex;
|
||||
auto &heap = heaps[heap_index];
|
||||
|
||||
// Naive searching is fine here as vkAllocate blocks are *huge* and we won't have many of them.
|
||||
auto itr = end(heap.blocks);
|
||||
if (dedicated_object == 0 && !external)
|
||||
{
|
||||
itr = find_if(begin(heap.blocks), end(heap.blocks),
|
||||
[=](const Allocation &alloc) { return size == alloc.size && memory_type == alloc.type && mode == alloc.mode; });
|
||||
}
|
||||
|
||||
bool host_visible = (mem_props.memoryTypes[memory_type].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0 &&
|
||||
host_memory != nullptr;
|
||||
|
||||
// Found previously used block.
|
||||
if (itr != end(heap.blocks))
|
||||
{
|
||||
*memory = itr->memory;
|
||||
if (host_visible)
|
||||
{
|
||||
if (table->vkMapMemory(device->get_device(), itr->memory, 0, VK_WHOLE_SIZE,
|
||||
0, reinterpret_cast<void **>(host_memory)) != VK_SUCCESS)
|
||||
return false;
|
||||
}
|
||||
heap.blocks.erase(itr);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Don't bother checking against budgets on external memory.
|
||||
// It's not very meaningful.
|
||||
if (!external)
|
||||
{
|
||||
HeapBudget budgets[VK_MAX_MEMORY_HEAPS];
|
||||
get_memory_budget_nolock(budgets);
|
||||
|
||||
#ifdef VULKAN_DEBUG
|
||||
LOGI("Allocating %.1f MiB on heap #%u (mode #%u), before allocating budget: (%.1f MiB / %.1f MiB) [%.1f / %.1f].\n",
|
||||
double(size) / double(1024 * 1024), heap_index, unsigned(mode),
|
||||
double(budgets[heap_index].device_usage) / double(1024 * 1024),
|
||||
double(budgets[heap_index].budget_size) / double(1024 * 1024),
|
||||
double(budgets[heap_index].tracked_usage) / double(1024 * 1024),
|
||||
double(budgets[heap_index].max_size) / double(1024 * 1024));
|
||||
#endif
|
||||
|
||||
const auto log_heap_index = [&]()
|
||||
{
|
||||
LOGW(" Size: %u MiB.\n", unsigned(size / (1024 * 1024)));
|
||||
LOGW(" Device usage: %u MiB.\n", unsigned(budgets[heap_index].device_usage / (1024 * 1024)));
|
||||
LOGW(" Tracked usage: %u MiB.\n", unsigned(budgets[heap_index].tracked_usage / (1024 * 1024)));
|
||||
LOGW(" Budget size: %u MiB.\n", unsigned(budgets[heap_index].budget_size / (1024 * 1024)));
|
||||
LOGW(" Max size: %u MiB.\n", unsigned(budgets[heap_index].max_size / (1024 * 1024)));
|
||||
};
|
||||
|
||||
// If we're going to blow out the budget, we should recycle a bit.
|
||||
if (budgets[heap_index].device_usage + size >= budgets[heap_index].budget_size)
|
||||
{
|
||||
LOGW("Will exceed memory budget, cleaning up ...\n");
|
||||
log_heap_index();
|
||||
heap.garbage_collect(device);
|
||||
}
|
||||
|
||||
get_memory_budget_nolock(budgets);
|
||||
if (budgets[heap_index].device_usage + size >= budgets[heap_index].budget_size)
|
||||
{
|
||||
LOGW("Even after garbage collection, we will exceed budget ...\n");
|
||||
if (memory_heap_is_budget_critical[heap_index])
|
||||
return false;
|
||||
log_heap_index();
|
||||
}
|
||||
}
|
||||
|
||||
VkMemoryAllocateInfo info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, nullptr, size, memory_type };
|
||||
VkMemoryDedicatedAllocateInfo dedicated = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO };
|
||||
VkExportMemoryAllocateInfo export_info = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO };
|
||||
VkMemoryPriorityAllocateInfoEXT priority_info = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };
|
||||
VkMemoryAllocateFlagsInfo flags_info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO };
|
||||
#ifdef _WIN32
|
||||
VkImportMemoryWin32HandleInfoKHR import_info = { VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR };
|
||||
#else
|
||||
VkImportMemoryFdInfoKHR import_info = { VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR };
|
||||
#endif
|
||||
|
||||
if (dedicated_object != 0)
|
||||
{
|
||||
if (object_type == VK_OBJECT_TYPE_IMAGE)
|
||||
dedicated.image = (VkImage)dedicated_object;
|
||||
else if (object_type == VK_OBJECT_TYPE_BUFFER)
|
||||
dedicated.buffer = (VkBuffer)dedicated_object;
|
||||
info.pNext = &dedicated;
|
||||
}
|
||||
|
||||
if (external)
|
||||
{
|
||||
VK_ASSERT(dedicated_object);
|
||||
|
||||
if (bool(*external))
|
||||
{
|
||||
import_info.handleType = external->memory_handle_type;
|
||||
import_info.pNext = info.pNext;
|
||||
info.pNext = &import_info;
|
||||
|
||||
#ifdef _WIN32
|
||||
import_info.handle = external->handle;
|
||||
#else
|
||||
import_info.fd = external->handle;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
export_info.handleTypes = external->memory_handle_type;
|
||||
export_info.pNext = info.pNext;
|
||||
info.pNext = &export_info;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't bother with memory priority on external objects.
|
||||
if (device->get_device_features().memory_priority_features.memoryPriority && !external)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case AllocationMode::LinearDeviceHighPriority:
|
||||
case AllocationMode::OptimalRenderTarget:
|
||||
priority_info.priority = 1.0f;
|
||||
break;
|
||||
|
||||
case AllocationMode::LinearDevice:
|
||||
case AllocationMode::OptimalResource:
|
||||
priority_info.priority = 0.5f;
|
||||
break;
|
||||
|
||||
default:
|
||||
priority_info.priority = 0.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
priority_info.pNext = info.pNext;
|
||||
info.pNext = &priority_info;
|
||||
}
|
||||
|
||||
if (device->get_device_features().vk12_features.bufferDeviceAddress &&
|
||||
allocation_mode_supports_bda(mode))
|
||||
{
|
||||
flags_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
|
||||
flags_info.pNext = info.pNext;
|
||||
info.pNext = &flags_info;
|
||||
}
|
||||
|
||||
VkDeviceMemory device_memory;
|
||||
VkResult res;
|
||||
{
|
||||
GRANITE_SCOPED_TIMELINE_EVENT_FILE(device->get_system_handles().timeline_trace_file, "vkAllocateMemory");
|
||||
res = table->vkAllocateMemory(device->get_device(), &info, nullptr, &device_memory);
|
||||
}
|
||||
|
||||
// If we're importing, make sure we consume the native handle.
|
||||
if (external && bool(*external) &&
|
||||
ExternalHandle::memory_handle_type_imports_by_reference(external->memory_handle_type))
|
||||
{
|
||||
#ifdef _WIN32
|
||||
::CloseHandle(external->handle);
|
||||
#else
|
||||
::close(external->handle);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (res == VK_SUCCESS)
|
||||
{
|
||||
heap.size += size;
|
||||
*memory = device_memory;
|
||||
|
||||
if (host_visible)
|
||||
{
|
||||
if (table->vkMapMemory(device->get_device(), device_memory, 0, VK_WHOLE_SIZE,
|
||||
0, reinterpret_cast<void **>(host_memory)) != VK_SUCCESS)
|
||||
{
|
||||
table->vkFreeMemory(device->get_device(), device_memory, nullptr);
|
||||
heap.size -= size;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Look through our heap and see if there are blocks of other types we can free.
|
||||
auto block_itr = begin(heap.blocks);
|
||||
while (res != VK_SUCCESS && itr != end(heap.blocks))
|
||||
{
|
||||
table->vkFreeMemory(device->get_device(), block_itr->memory, nullptr);
|
||||
heap.size -= block_itr->size;
|
||||
{
|
||||
GRANITE_SCOPED_TIMELINE_EVENT_FILE(device->get_system_handles().timeline_trace_file,
|
||||
"vkAllocateMemory");
|
||||
res = table->vkAllocateMemory(device->get_device(), &info, nullptr, &device_memory);
|
||||
}
|
||||
++block_itr;
|
||||
}
|
||||
|
||||
heap.blocks.erase(begin(heap.blocks), block_itr);
|
||||
|
||||
if (res == VK_SUCCESS)
|
||||
{
|
||||
heap.size += size;
|
||||
*memory = device_memory;
|
||||
|
||||
if (host_visible)
|
||||
{
|
||||
if (table->vkMapMemory(device->get_device(), device_memory, 0, size, 0, reinterpret_cast<void **>(host_memory)) !=
|
||||
VK_SUCCESS)
|
||||
{
|
||||
table->vkFreeMemory(device->get_device(), device_memory, nullptr);
|
||||
heap.size -= size;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DeviceAllocationOwner::DeviceAllocationOwner(Device *device_, const DeviceAllocation &alloc_)
|
||||
: device(device_), alloc(alloc_)
|
||||
{
|
||||
}
|
||||
|
||||
DeviceAllocationOwner::~DeviceAllocationOwner()
|
||||
{
|
||||
if (alloc.get_memory())
|
||||
device->free_memory(alloc);
|
||||
}
|
||||
|
||||
const DeviceAllocation &DeviceAllocationOwner::get_allocation() const
|
||||
{
|
||||
return alloc;
|
||||
}
|
||||
|
||||
void DeviceAllocationDeleter::operator()(DeviceAllocationOwner *owner)
|
||||
{
|
||||
owner->device->handle_pool.allocations.free(owner);
|
||||
}
|
||||
}
|
||||
300
external/parallel-rdp/parallel-rdp-standalone/vulkan/memory_allocator.hpp
vendored
Normal file
300
external/parallel-rdp/parallel-rdp-standalone/vulkan/memory_allocator.hpp
vendored
Normal file
@@ -0,0 +1,300 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "intrusive.hpp"
|
||||
#include "object_pool.hpp"
|
||||
#include "intrusive_list.hpp"
|
||||
#include "vulkan_headers.hpp"
|
||||
#include "logging.hpp"
|
||||
#include "bitops.hpp"
|
||||
#include "enum_cast.hpp"
|
||||
#include "vulkan_common.hpp"
|
||||
#include "arena_allocator.hpp"
|
||||
#include <assert.h>
|
||||
#include <memory>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class Device;
|
||||
|
||||
enum class MemoryClass : uint8_t
|
||||
{
|
||||
Small = 0,
|
||||
Medium,
|
||||
Large,
|
||||
Huge,
|
||||
Count
|
||||
};
|
||||
|
||||
enum class AllocationMode : uint8_t
|
||||
{
|
||||
LinearHostMappable = 0,
|
||||
LinearDevice,
|
||||
LinearDeviceHighPriority,
|
||||
OptimalResource,
|
||||
OptimalRenderTarget,
|
||||
External,
|
||||
Count
|
||||
};
|
||||
|
||||
enum MemoryAccessFlag : uint32_t
|
||||
{
|
||||
MEMORY_ACCESS_WRITE_BIT = 1,
|
||||
MEMORY_ACCESS_READ_BIT = 2,
|
||||
MEMORY_ACCESS_READ_WRITE_BIT = MEMORY_ACCESS_WRITE_BIT | MEMORY_ACCESS_READ_BIT
|
||||
};
|
||||
using MemoryAccessFlags = uint32_t;
|
||||
|
||||
struct DeviceAllocation;
|
||||
class DeviceAllocator;
|
||||
|
||||
class ClassAllocator;
|
||||
class DeviceAllocator;
|
||||
class Allocator;
|
||||
class Device;
|
||||
|
||||
using MiniHeap = Util::LegionHeap<DeviceAllocation>;
|
||||
|
||||
struct DeviceAllocation
|
||||
{
|
||||
friend class Util::ArenaAllocator<ClassAllocator, DeviceAllocation>;
|
||||
friend class ClassAllocator;
|
||||
friend class Allocator;
|
||||
friend class DeviceAllocator;
|
||||
friend class Device;
|
||||
friend class ImageResourceHolder;
|
||||
|
||||
public:
|
||||
inline VkDeviceMemory get_memory() const
|
||||
{
|
||||
return base;
|
||||
}
|
||||
|
||||
inline bool allocation_is_global() const
|
||||
{
|
||||
return !alloc && base;
|
||||
}
|
||||
|
||||
inline uint32_t get_offset() const
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
|
||||
inline uint32_t get_size() const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
inline uint32_t get_mask() const
|
||||
{
|
||||
return mask;
|
||||
}
|
||||
|
||||
inline bool is_host_allocation() const
|
||||
{
|
||||
return host_base != nullptr;
|
||||
}
|
||||
|
||||
static DeviceAllocation make_imported_allocation(VkDeviceMemory memory, VkDeviceSize size, uint32_t memory_type);
|
||||
|
||||
ExternalHandle export_handle(Device &device);
|
||||
|
||||
private:
|
||||
VkDeviceMemory base = VK_NULL_HANDLE;
|
||||
uint8_t *host_base = nullptr;
|
||||
ClassAllocator *alloc = nullptr;
|
||||
Util::IntrusiveList<MiniHeap>::Iterator heap = {};
|
||||
uint32_t offset = 0;
|
||||
uint32_t mask = 0;
|
||||
uint32_t size = 0;
|
||||
VkExternalMemoryHandleTypeFlags exportable_types = 0;
|
||||
|
||||
AllocationMode mode = AllocationMode::Count;
|
||||
uint8_t memory_type = 0;
|
||||
|
||||
void free_global(DeviceAllocator &allocator, uint32_t size, uint32_t memory_type);
|
||||
void free_immediate();
|
||||
void free_immediate(DeviceAllocator &allocator);
|
||||
};
|
||||
|
||||
class DeviceAllocationOwner;
|
||||
struct DeviceAllocationDeleter
|
||||
{
|
||||
void operator()(DeviceAllocationOwner *owner);
|
||||
};
|
||||
|
||||
class DeviceAllocationOwner : public Util::IntrusivePtrEnabled<DeviceAllocationOwner, DeviceAllocationDeleter, HandleCounter>
|
||||
{
|
||||
public:
|
||||
friend class Util::ObjectPool<DeviceAllocationOwner>;
|
||||
friend struct DeviceAllocationDeleter;
|
||||
|
||||
~DeviceAllocationOwner();
|
||||
const DeviceAllocation &get_allocation() const;
|
||||
|
||||
private:
|
||||
DeviceAllocationOwner(Device *device, const DeviceAllocation &alloc);
|
||||
Device *device;
|
||||
DeviceAllocation alloc;
|
||||
};
|
||||
using DeviceAllocationOwnerHandle = Util::IntrusivePtr<DeviceAllocationOwner>;
|
||||
|
||||
struct MemoryAllocateInfo
|
||||
{
|
||||
VkMemoryRequirements requirements = {};
|
||||
VkMemoryPropertyFlags required_properties = 0;
|
||||
AllocationMode mode = {};
|
||||
};
|
||||
|
||||
class ClassAllocator : public Util::ArenaAllocator<ClassAllocator, DeviceAllocation>
|
||||
{
|
||||
public:
|
||||
friend class Util::ArenaAllocator<ClassAllocator, DeviceAllocation>;
|
||||
|
||||
inline void set_global_allocator(DeviceAllocator *allocator, AllocationMode mode, uint32_t memory_type_)
|
||||
{
|
||||
global_allocator = allocator;
|
||||
global_allocator_mode = mode;
|
||||
memory_type = memory_type_;
|
||||
}
|
||||
|
||||
inline void set_parent(ClassAllocator *allocator)
|
||||
{
|
||||
parent = allocator;
|
||||
}
|
||||
|
||||
private:
|
||||
ClassAllocator *parent = nullptr;
|
||||
uint32_t memory_type = 0;
|
||||
DeviceAllocator *global_allocator = nullptr;
|
||||
AllocationMode global_allocator_mode = AllocationMode::Count;
|
||||
|
||||
// Implements curious recurring template pattern calls.
|
||||
bool allocate_backing_heap(DeviceAllocation *allocation);
|
||||
void free_backing_heap(DeviceAllocation *allocation);
|
||||
void prepare_allocation(DeviceAllocation *allocation, Util::IntrusiveList<MiniHeap>::Iterator heap_itr,
|
||||
const Util::SuballocationResult &suballoc);
|
||||
};
|
||||
|
||||
class Allocator
|
||||
{
|
||||
public:
|
||||
explicit Allocator(Util::ObjectPool<MiniHeap> &object_pool);
|
||||
void operator=(const Allocator &) = delete;
|
||||
Allocator(const Allocator &) = delete;
|
||||
|
||||
bool allocate(uint32_t size, uint32_t alignment, AllocationMode mode, DeviceAllocation *alloc);
|
||||
bool allocate_global(uint32_t size, AllocationMode mode, DeviceAllocation *alloc);
|
||||
bool allocate_dedicated(uint32_t size, AllocationMode mode, DeviceAllocation *alloc,
|
||||
VkObjectType object_type, uint64_t object, ExternalHandle *external);
|
||||
|
||||
inline ClassAllocator &get_class_allocator(MemoryClass clazz, AllocationMode mode)
|
||||
{
|
||||
return classes[unsigned(clazz)][unsigned(mode)];
|
||||
}
|
||||
|
||||
static void free(DeviceAllocation *alloc)
|
||||
{
|
||||
alloc->free_immediate();
|
||||
}
|
||||
|
||||
void set_global_allocator(DeviceAllocator *allocator, uint32_t memory_type_)
|
||||
{
|
||||
memory_type = memory_type_;
|
||||
for (auto &sub : classes)
|
||||
for (int i = 0; i < Util::ecast(AllocationMode::Count); i++)
|
||||
sub[i].set_global_allocator(allocator, AllocationMode(i), memory_type);
|
||||
global_allocator = allocator;
|
||||
}
|
||||
|
||||
private:
|
||||
ClassAllocator classes[Util::ecast(MemoryClass::Count)][Util::ecast(AllocationMode::Count)];
|
||||
DeviceAllocator *global_allocator = nullptr;
|
||||
uint32_t memory_type = 0;
|
||||
};
|
||||
|
||||
struct HeapBudget
|
||||
{
|
||||
VkDeviceSize max_size;
|
||||
VkDeviceSize budget_size;
|
||||
VkDeviceSize tracked_usage;
|
||||
VkDeviceSize device_usage;
|
||||
};
|
||||
|
||||
class DeviceAllocator
|
||||
{
|
||||
public:
|
||||
void init(Device *device);
|
||||
|
||||
~DeviceAllocator();
|
||||
|
||||
bool allocate_generic_memory(uint32_t size, uint32_t alignment, AllocationMode mode, uint32_t memory_type,
|
||||
DeviceAllocation *alloc);
|
||||
bool allocate_buffer_memory(uint32_t size, uint32_t alignment, AllocationMode mode, uint32_t memory_type,
|
||||
VkBuffer buffer, DeviceAllocation *alloc, ExternalHandle *external);
|
||||
bool allocate_image_memory(uint32_t size, uint32_t alignment, AllocationMode mode, uint32_t memory_type,
|
||||
VkImage image, bool force_no_dedicated, DeviceAllocation *alloc, ExternalHandle *external);
|
||||
|
||||
void garbage_collect();
|
||||
void *map_memory(const DeviceAllocation &alloc, MemoryAccessFlags flags, VkDeviceSize offset, VkDeviceSize length);
|
||||
void unmap_memory(const DeviceAllocation &alloc, MemoryAccessFlags flags, VkDeviceSize offset, VkDeviceSize length);
|
||||
|
||||
void get_memory_budget(HeapBudget *heaps);
|
||||
|
||||
bool internal_allocate(uint32_t size, uint32_t memory_type, AllocationMode mode,
|
||||
VkDeviceMemory *memory, uint8_t **host_memory,
|
||||
VkObjectType object_type, uint64_t dedicated_object, ExternalHandle *external);
|
||||
void internal_free(uint32_t size, uint32_t memory_type, AllocationMode mode, VkDeviceMemory memory, bool is_mapped);
|
||||
void internal_free_no_recycle(uint32_t size, uint32_t memory_type, VkDeviceMemory memory);
|
||||
|
||||
private:
|
||||
Util::ObjectPool<MiniHeap> object_pool;
|
||||
std::vector<std::unique_ptr<Allocator>> allocators;
|
||||
Device *device = nullptr;
|
||||
const VolkDeviceTable *table = nullptr;
|
||||
VkPhysicalDeviceMemoryProperties mem_props;
|
||||
VkDeviceSize atom_alignment = 1;
|
||||
struct Allocation
|
||||
{
|
||||
VkDeviceMemory memory;
|
||||
uint32_t size;
|
||||
uint32_t type;
|
||||
AllocationMode mode;
|
||||
};
|
||||
|
||||
struct Heap
|
||||
{
|
||||
uint64_t size = 0;
|
||||
std::vector<Allocation> blocks;
|
||||
void garbage_collect(Device *device);
|
||||
};
|
||||
|
||||
std::vector<Heap> heaps;
|
||||
bool memory_heap_is_budget_critical[VK_MAX_MEMORY_HEAPS] = {};
|
||||
void get_memory_budget_nolock(HeapBudget *heaps);
|
||||
};
|
||||
}
|
||||
43
external/parallel-rdp/parallel-rdp-standalone/vulkan/pipeline_event.cpp
vendored
Normal file
43
external/parallel-rdp/parallel-rdp-standalone/vulkan/pipeline_event.cpp
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "pipeline_event.hpp"
|
||||
#include "device.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
EventHolder::~EventHolder()
|
||||
{
|
||||
if (event)
|
||||
{
|
||||
if (internal_sync)
|
||||
device->destroy_event_nolock(event);
|
||||
else
|
||||
device->destroy_event(event);
|
||||
}
|
||||
}
|
||||
|
||||
void EventHolderDeleter::operator()(Vulkan::EventHolder *event)
|
||||
{
|
||||
event->device->handle_pool.events.free(event);
|
||||
}
|
||||
}
|
||||
67
external/parallel-rdp/parallel-rdp-standalone/vulkan/pipeline_event.hpp
vendored
Normal file
67
external/parallel-rdp/parallel-rdp-standalone/vulkan/pipeline_event.hpp
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vulkan_headers.hpp"
|
||||
#include "vulkan_common.hpp"
|
||||
#include "cookie.hpp"
|
||||
#include "object_pool.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class Device;
|
||||
class EventHolder;
|
||||
|
||||
struct EventHolderDeleter
|
||||
{
|
||||
void operator()(EventHolder *event);
|
||||
};
|
||||
|
||||
class EventHolder : public Util::IntrusivePtrEnabled<EventHolder, EventHolderDeleter, HandleCounter>,
|
||||
public InternalSyncEnabled
|
||||
{
|
||||
public:
|
||||
friend struct EventHolderDeleter;
|
||||
|
||||
~EventHolder();
|
||||
|
||||
const VkEvent &get_event() const
|
||||
{
|
||||
return event;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Util::ObjectPool<EventHolder>;
|
||||
EventHolder(Device *device_, VkEvent event_)
|
||||
: device(device_)
|
||||
, event(event_)
|
||||
{
|
||||
}
|
||||
|
||||
Device *device;
|
||||
VkEvent event;
|
||||
};
|
||||
|
||||
using PipelineEvent = Util::IntrusivePtr<EventHolder>;
|
||||
|
||||
}
|
||||
528
external/parallel-rdp/parallel-rdp-standalone/vulkan/query_pool.cpp
vendored
Normal file
528
external/parallel-rdp/parallel-rdp-standalone/vulkan/query_pool.cpp
vendored
Normal file
@@ -0,0 +1,528 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "query_pool.hpp"
|
||||
#include "device.hpp"
|
||||
#include <utility>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
static const char *storage_to_str(VkPerformanceCounterStorageKHR storage)
|
||||
{
|
||||
switch (storage)
|
||||
{
|
||||
case VK_PERFORMANCE_COUNTER_STORAGE_FLOAT32_KHR:
|
||||
return "float32";
|
||||
case VK_PERFORMANCE_COUNTER_STORAGE_FLOAT64_KHR:
|
||||
return "float64";
|
||||
case VK_PERFORMANCE_COUNTER_STORAGE_INT32_KHR:
|
||||
return "int32";
|
||||
case VK_PERFORMANCE_COUNTER_STORAGE_INT64_KHR:
|
||||
return "int64";
|
||||
case VK_PERFORMANCE_COUNTER_STORAGE_UINT32_KHR:
|
||||
return "uint32";
|
||||
case VK_PERFORMANCE_COUNTER_STORAGE_UINT64_KHR:
|
||||
return "uint64";
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *scope_to_str(VkPerformanceCounterScopeKHR scope)
|
||||
{
|
||||
switch (scope)
|
||||
{
|
||||
case VK_QUERY_SCOPE_COMMAND_BUFFER_KHR:
|
||||
return "command buffer";
|
||||
case VK_QUERY_SCOPE_RENDER_PASS_KHR:
|
||||
return "render pass";
|
||||
case VK_QUERY_SCOPE_COMMAND_KHR:
|
||||
return "command";
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *unit_to_str(VkPerformanceCounterUnitKHR unit)
|
||||
{
|
||||
switch (unit)
|
||||
{
|
||||
case VK_PERFORMANCE_COUNTER_UNIT_AMPS_KHR:
|
||||
return "A";
|
||||
case VK_PERFORMANCE_COUNTER_UNIT_BYTES_KHR:
|
||||
return "bytes";
|
||||
case VK_PERFORMANCE_COUNTER_UNIT_BYTES_PER_SECOND_KHR:
|
||||
return "bytes / second";
|
||||
case VK_PERFORMANCE_COUNTER_UNIT_CYCLES_KHR:
|
||||
return "cycles";
|
||||
case VK_PERFORMANCE_COUNTER_UNIT_GENERIC_KHR:
|
||||
return "units";
|
||||
case VK_PERFORMANCE_COUNTER_UNIT_HERTZ_KHR:
|
||||
return "Hz";
|
||||
case VK_PERFORMANCE_COUNTER_UNIT_KELVIN_KHR:
|
||||
return "K";
|
||||
case VK_PERFORMANCE_COUNTER_UNIT_NANOSECONDS_KHR:
|
||||
return "ns";
|
||||
case VK_PERFORMANCE_COUNTER_UNIT_PERCENTAGE_KHR:
|
||||
return "%";
|
||||
case VK_PERFORMANCE_COUNTER_UNIT_VOLTS_KHR:
|
||||
return "V";
|
||||
case VK_PERFORMANCE_COUNTER_UNIT_WATTS_KHR:
|
||||
return "W";
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
||||
void PerformanceQueryPool::log_available_counters(const VkPerformanceCounterKHR *counters,
|
||||
const VkPerformanceCounterDescriptionKHR *descs,
|
||||
uint32_t count)
|
||||
{
|
||||
for (uint32_t i = 0; i < count; i++)
|
||||
{
|
||||
LOGI(" %s: %s\n", descs[i].name, descs[i].description);
|
||||
LOGI(" Storage: %s\n", storage_to_str(counters[i].storage));
|
||||
LOGI(" Scope: %s\n", scope_to_str(counters[i].scope));
|
||||
LOGI(" Unit: %s\n", unit_to_str(counters[i].unit));
|
||||
}
|
||||
}
|
||||
|
||||
void PerformanceQueryPool::init_device(Device *device_, uint32_t queue_family_index_)
|
||||
{
|
||||
device = device_;
|
||||
queue_family_index = queue_family_index_;
|
||||
|
||||
if (!device->get_device_features().performance_query_features.performanceCounterQueryPools)
|
||||
return;
|
||||
|
||||
uint32_t num_counters = 0;
|
||||
if (vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR(
|
||||
device->get_physical_device(),
|
||||
queue_family_index,
|
||||
&num_counters,
|
||||
nullptr, nullptr) != VK_SUCCESS)
|
||||
{
|
||||
LOGE("Failed to enumerate performance counters.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
counters.resize(num_counters, { VK_STRUCTURE_TYPE_PERFORMANCE_COUNTER_KHR });
|
||||
counter_descriptions.resize(num_counters, { VK_STRUCTURE_TYPE_PERFORMANCE_COUNTER_DESCRIPTION_KHR });
|
||||
|
||||
if (vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR(
|
||||
device->get_physical_device(),
|
||||
queue_family_index,
|
||||
&num_counters,
|
||||
counters.data(), counter_descriptions.data()) != VK_SUCCESS)
|
||||
{
|
||||
LOGE("Failed to enumerate performance counters.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PerformanceQueryPool::~PerformanceQueryPool()
|
||||
{
|
||||
if (pool)
|
||||
device->get_device_table().vkDestroyQueryPool(device->get_device(), pool, nullptr);
|
||||
}
|
||||
|
||||
void PerformanceQueryPool::begin_command_buffer(VkCommandBuffer cmd)
|
||||
{
|
||||
if (!pool)
|
||||
return;
|
||||
|
||||
auto &table = device->get_device_table();
|
||||
table.vkResetQueryPoolEXT(device->get_device(), pool, 0, 1);
|
||||
table.vkCmdBeginQuery(cmd, pool, 0, 0);
|
||||
|
||||
VkMemoryBarrier barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER };
|
||||
barrier.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_MEMORY_READ_BIT;
|
||||
table.vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
0, 1, &barrier, 0, nullptr, 0, nullptr);
|
||||
}
|
||||
|
||||
void PerformanceQueryPool::end_command_buffer(VkCommandBuffer cmd)
|
||||
{
|
||||
if (!pool)
|
||||
return;
|
||||
|
||||
auto &table = device->get_device_table();
|
||||
|
||||
VkMemoryBarrier barrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER };
|
||||
barrier.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_MEMORY_READ_BIT;
|
||||
table.vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
0, 1, &barrier, 0, nullptr, 0, nullptr);
|
||||
table.vkCmdEndQuery(cmd, pool, 0);
|
||||
}
|
||||
|
||||
void PerformanceQueryPool::report()
|
||||
{
|
||||
if (pool == VK_NULL_HANDLE)
|
||||
{
|
||||
LOGE("No query pool is set up.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto &table = device->get_device_table();
|
||||
if (table.vkGetQueryPoolResults(device->get_device(), pool,
|
||||
0, 1,
|
||||
results.size() * sizeof(VkPerformanceCounterResultKHR),
|
||||
results.data(),
|
||||
sizeof(VkPerformanceCounterResultKHR),
|
||||
VK_QUERY_RESULT_WAIT_BIT) != VK_SUCCESS)
|
||||
{
|
||||
LOGE("Getting performance counters did not succeed.\n");
|
||||
}
|
||||
|
||||
size_t num_counters = results.size();
|
||||
|
||||
LOGI("\n=== Profiling result ===\n");
|
||||
for (size_t i = 0; i < num_counters; i++)
|
||||
{
|
||||
auto &counter = counters[active_indices[i]];
|
||||
auto &desc = counter_descriptions[active_indices[i]];
|
||||
|
||||
switch (counter.storage)
|
||||
{
|
||||
case VK_PERFORMANCE_COUNTER_STORAGE_INT32_KHR:
|
||||
LOGI(" %s (%s): %d %s\n", desc.name, desc.description, results[i].int32, unit_to_str(counter.unit));
|
||||
break;
|
||||
case VK_PERFORMANCE_COUNTER_STORAGE_INT64_KHR:
|
||||
LOGI(" %s (%s): %lld %s\n", desc.name, desc.description, static_cast<long long>(results[i].int64), unit_to_str(counter.unit));
|
||||
break;
|
||||
case VK_PERFORMANCE_COUNTER_STORAGE_UINT32_KHR:
|
||||
LOGI(" %s (%s): %u %s\n", desc.name, desc.description, results[i].uint32, unit_to_str(counter.unit));
|
||||
break;
|
||||
case VK_PERFORMANCE_COUNTER_STORAGE_UINT64_KHR:
|
||||
LOGI(" %s (%s): %llu %s\n", desc.name, desc.description, static_cast<long long>(results[i].uint64), unit_to_str(counter.unit));
|
||||
break;
|
||||
case VK_PERFORMANCE_COUNTER_STORAGE_FLOAT32_KHR:
|
||||
LOGI(" %s (%s): %g %s\n", desc.name, desc.description, results[i].float32, unit_to_str(counter.unit));
|
||||
break;
|
||||
case VK_PERFORMANCE_COUNTER_STORAGE_FLOAT64_KHR:
|
||||
LOGI(" %s (%s): %g %s\n", desc.name, desc.description, results[i].float64, unit_to_str(counter.unit));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
LOGI("================================\n\n");
|
||||
}
|
||||
|
||||
uint32_t PerformanceQueryPool::get_num_counters() const
|
||||
{
|
||||
return uint32_t(counters.size());
|
||||
}
|
||||
|
||||
const VkPerformanceCounterKHR *PerformanceQueryPool::get_available_counters() const
|
||||
{
|
||||
return counters.data();
|
||||
}
|
||||
|
||||
const VkPerformanceCounterDescriptionKHR *PerformanceQueryPool::get_available_counter_descs() const
|
||||
{
|
||||
return counter_descriptions.data();
|
||||
}
|
||||
|
||||
bool PerformanceQueryPool::init_counters(const std::vector<std::string> &counter_names)
|
||||
{
|
||||
if (!device->get_device_features().performance_query_features.performanceCounterQueryPools)
|
||||
{
|
||||
LOGE("Device does not support VK_KHR_performance_query.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!device->get_device_features().vk12_features.hostQueryReset)
|
||||
{
|
||||
LOGE("Device does not support host query reset.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto &table = device->get_device_table();
|
||||
if (pool)
|
||||
table.vkDestroyQueryPool(device->get_device(), pool, nullptr);
|
||||
pool = VK_NULL_HANDLE;
|
||||
|
||||
VkQueryPoolPerformanceCreateInfoKHR performance_info = { VK_STRUCTURE_TYPE_QUERY_POOL_PERFORMANCE_CREATE_INFO_KHR };
|
||||
VkQueryPoolCreateInfo info = { VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO };
|
||||
info.pNext = &performance_info;
|
||||
|
||||
info.queryType = VK_QUERY_TYPE_PERFORMANCE_QUERY_KHR;
|
||||
info.queryCount = 1;
|
||||
|
||||
active_indices.clear();
|
||||
|
||||
for (auto &name : counter_names)
|
||||
{
|
||||
auto itr = find_if(begin(counter_descriptions), end(counter_descriptions), [&](const VkPerformanceCounterDescriptionKHR &desc) {
|
||||
return name == desc.name;
|
||||
});
|
||||
|
||||
if (itr != end(counter_descriptions))
|
||||
{
|
||||
LOGI("Found counter %s: %s\n", itr->name, itr->description);
|
||||
active_indices.push_back(itr - begin(counter_descriptions));
|
||||
}
|
||||
}
|
||||
|
||||
if (active_indices.empty())
|
||||
{
|
||||
LOGW("No performance counters were enabled.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
performance_info.queueFamilyIndex = queue_family_index;
|
||||
performance_info.counterIndexCount = active_indices.size();
|
||||
performance_info.pCounterIndices = active_indices.data();
|
||||
results.resize(active_indices.size());
|
||||
|
||||
uint32_t num_passes = 0;
|
||||
vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR(device->get_physical_device(),
|
||||
&performance_info, &num_passes);
|
||||
|
||||
if (num_passes != 1)
|
||||
{
|
||||
LOGE("Implementation requires %u passes to query performance counters. Cannot create query pool.\n",
|
||||
num_passes);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (table.vkCreateQueryPool(device->get_device(), &info, nullptr, &pool) != VK_SUCCESS)
|
||||
{
|
||||
LOGE("Failed to create performance query pool.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QueryPool::QueryPool(Device *device_)
|
||||
: device(device_)
|
||||
, table(device_->get_device_table())
|
||||
{
|
||||
supports_timestamp = device->get_gpu_properties().limits.timestampComputeAndGraphics &&
|
||||
device->get_device_features().vk12_features.hostQueryReset;
|
||||
|
||||
// Ignore timestampValidBits and friends for now.
|
||||
if (supports_timestamp)
|
||||
add_pool();
|
||||
}
|
||||
|
||||
QueryPool::~QueryPool()
|
||||
{
|
||||
for (auto &pool : pools)
|
||||
table.vkDestroyQueryPool(device->get_device(), pool.pool, nullptr);
|
||||
}
|
||||
|
||||
void QueryPool::begin()
|
||||
{
|
||||
for (unsigned i = 0; i <= pool_index; i++)
|
||||
{
|
||||
if (i >= pools.size())
|
||||
continue;
|
||||
|
||||
auto &pool = pools[i];
|
||||
if (pool.index == 0)
|
||||
continue;
|
||||
|
||||
table.vkGetQueryPoolResults(device->get_device(), pool.pool,
|
||||
0, pool.index,
|
||||
pool.index * sizeof(uint64_t),
|
||||
pool.query_results.data(),
|
||||
sizeof(uint64_t),
|
||||
VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
|
||||
|
||||
for (unsigned j = 0; j < pool.index; j++)
|
||||
pool.cookies[j]->signal_timestamp_ticks(pool.query_results[j]);
|
||||
|
||||
table.vkResetQueryPool(device->get_device(), pool.pool, 0, pool.index);
|
||||
}
|
||||
|
||||
pool_index = 0;
|
||||
for (auto &pool : pools)
|
||||
pool.index = 0;
|
||||
}
|
||||
|
||||
void QueryPool::add_pool()
|
||||
{
|
||||
VkQueryPoolCreateInfo pool_info = { VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO };
|
||||
pool_info.queryType = VK_QUERY_TYPE_TIMESTAMP;
|
||||
pool_info.queryCount = 64;
|
||||
|
||||
Pool pool;
|
||||
table.vkCreateQueryPool(device->get_device(), &pool_info, nullptr, &pool.pool);
|
||||
pool.size = pool_info.queryCount;
|
||||
pool.index = 0;
|
||||
pool.query_results.resize(pool.size);
|
||||
pool.cookies.resize(pool.size);
|
||||
|
||||
table.vkResetQueryPool(device->get_device(), pool.pool, 0, pool.size);
|
||||
|
||||
pools.push_back(std::move(pool));
|
||||
}
|
||||
|
||||
QueryPoolHandle QueryPool::write_timestamp(VkCommandBuffer cmd, VkPipelineStageFlags2 stage)
|
||||
{
|
||||
if (!supports_timestamp)
|
||||
{
|
||||
LOGI("Timestamps are not supported on this implementation.\n");
|
||||
return {};
|
||||
}
|
||||
|
||||
VK_ASSERT((stage & (stage - 1)) == 0);
|
||||
|
||||
if (pools[pool_index].index >= pools[pool_index].size)
|
||||
pool_index++;
|
||||
|
||||
if (pool_index >= pools.size())
|
||||
add_pool();
|
||||
|
||||
auto &pool = pools[pool_index];
|
||||
|
||||
auto cookie = QueryPoolHandle(device->handle_pool.query.allocate(device, true));
|
||||
pool.cookies[pool.index] = cookie;
|
||||
|
||||
if (device->get_device_features().vk13_features.synchronization2)
|
||||
table.vkCmdWriteTimestamp2(cmd, stage, pool.pool, pool.index);
|
||||
else
|
||||
{
|
||||
table.vkCmdWriteTimestamp(cmd, static_cast<VkPipelineStageFlagBits>(convert_vk_src_stage2(stage)),
|
||||
pool.pool, pool.index);
|
||||
}
|
||||
|
||||
pool.index++;
|
||||
return cookie;
|
||||
}
|
||||
|
||||
void QueryPoolResultDeleter::operator()(QueryPoolResult *query)
|
||||
{
|
||||
query->device->handle_pool.query.free(query);
|
||||
}
|
||||
|
||||
void TimestampInterval::mark_end_of_frame_context()
|
||||
{
|
||||
if (total_time > 0.0)
|
||||
total_frame_iterations++;
|
||||
}
|
||||
|
||||
uint64_t TimestampInterval::get_total_accumulations() const
|
||||
{
|
||||
return total_accumulations;
|
||||
}
|
||||
|
||||
uint64_t TimestampInterval::get_total_frame_iterations() const
|
||||
{
|
||||
return total_frame_iterations;
|
||||
}
|
||||
|
||||
double TimestampInterval::get_total_time() const
|
||||
{
|
||||
return total_time;
|
||||
}
|
||||
|
||||
void TimestampInterval::accumulate_time(double t)
|
||||
{
|
||||
total_time += t;
|
||||
total_accumulations++;
|
||||
}
|
||||
|
||||
double TimestampInterval::get_time_per_iteration() const
|
||||
{
|
||||
if (total_frame_iterations)
|
||||
return total_time / double(total_frame_iterations);
|
||||
else
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double TimestampInterval::get_time_per_accumulation() const
|
||||
{
|
||||
if (total_accumulations)
|
||||
return total_time / double(total_accumulations);
|
||||
else
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
const std::string &TimestampInterval::get_tag() const
|
||||
{
|
||||
return tag;
|
||||
}
|
||||
|
||||
void TimestampInterval::reset()
|
||||
{
|
||||
total_time = 0.0;
|
||||
total_accumulations = 0;
|
||||
total_frame_iterations = 0;
|
||||
}
|
||||
|
||||
TimestampInterval::TimestampInterval(std::string tag_)
|
||||
: tag(std::move(tag_))
|
||||
{
|
||||
}
|
||||
|
||||
TimestampInterval *TimestampIntervalManager::get_timestamp_tag(const char *tag)
|
||||
{
|
||||
Util::Hasher h;
|
||||
h.string(tag);
|
||||
return timestamps.emplace_yield(h.get(), tag);
|
||||
}
|
||||
|
||||
void TimestampIntervalManager::mark_end_of_frame_context()
|
||||
{
|
||||
for (auto ×tamp : timestamps)
|
||||
timestamp.mark_end_of_frame_context();
|
||||
}
|
||||
|
||||
void TimestampIntervalManager::reset()
|
||||
{
|
||||
for (auto ×tamp : timestamps)
|
||||
timestamp.reset();
|
||||
}
|
||||
|
||||
void TimestampIntervalManager::log_simple(const TimestampIntervalReportCallback &func) const
|
||||
{
|
||||
for (auto ×tamp : timestamps)
|
||||
{
|
||||
if (timestamp.get_total_frame_iterations())
|
||||
{
|
||||
TimestampIntervalReport report = {};
|
||||
report.time_per_accumulation = timestamp.get_time_per_accumulation();
|
||||
report.time_per_frame_context = timestamp.get_time_per_iteration();
|
||||
report.accumulations_per_frame_context =
|
||||
double(timestamp.get_total_accumulations()) / double(timestamp.get_total_frame_iterations());
|
||||
|
||||
if (func)
|
||||
{
|
||||
func(timestamp.get_tag(), report);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGI("Timestamp tag report: %s\n", timestamp.get_tag().c_str());
|
||||
LOGI(" %.3f ms / iteration\n", 1000.0 * report.time_per_accumulation);
|
||||
LOGI(" %.3f ms / frame context\n", 1000.0 * report.time_per_frame_context);
|
||||
LOGI(" %.3f iterations / frame context\n", report.accumulations_per_frame_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
186
external/parallel-rdp/parallel-rdp-standalone/vulkan/query_pool.hpp
vendored
Normal file
186
external/parallel-rdp/parallel-rdp-standalone/vulkan/query_pool.hpp
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vulkan_headers.hpp"
|
||||
#include "vulkan_common.hpp"
|
||||
#include "object_pool.hpp"
|
||||
#include <functional>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class Device;
|
||||
|
||||
class PerformanceQueryPool
|
||||
{
|
||||
public:
|
||||
void init_device(Device *device, uint32_t queue_family_index);
|
||||
~PerformanceQueryPool();
|
||||
bool init_counters(const std::vector<std::string> &enable_counter_names);
|
||||
|
||||
void begin_command_buffer(VkCommandBuffer cmd);
|
||||
void end_command_buffer(VkCommandBuffer cmd);
|
||||
|
||||
void report();
|
||||
|
||||
uint32_t get_num_counters() const;
|
||||
const VkPerformanceCounterKHR *get_available_counters() const;
|
||||
const VkPerformanceCounterDescriptionKHR *get_available_counter_descs() const;
|
||||
|
||||
static void log_available_counters(const VkPerformanceCounterKHR *counters,
|
||||
const VkPerformanceCounterDescriptionKHR *descs,
|
||||
uint32_t count);
|
||||
|
||||
private:
|
||||
Device *device = nullptr;
|
||||
uint32_t queue_family_index = 0;
|
||||
VkQueryPool pool = VK_NULL_HANDLE;
|
||||
std::vector<VkPerformanceCounterResultKHR> results;
|
||||
std::vector<VkPerformanceCounterKHR> counters;
|
||||
std::vector<VkPerformanceCounterDescriptionKHR> counter_descriptions;
|
||||
std::vector<uint32_t> active_indices;
|
||||
};
|
||||
|
||||
class QueryPoolResult;
|
||||
|
||||
struct QueryPoolResultDeleter
|
||||
{
|
||||
void operator()(QueryPoolResult *query);
|
||||
};
|
||||
|
||||
class QueryPoolResult : public Util::IntrusivePtrEnabled<QueryPoolResult, QueryPoolResultDeleter, HandleCounter>
|
||||
{
|
||||
public:
|
||||
friend struct QueryPoolResultDeleter;
|
||||
|
||||
void signal_timestamp_ticks(uint64_t ticks)
|
||||
{
|
||||
timestamp_ticks = ticks;
|
||||
has_timestamp = true;
|
||||
}
|
||||
|
||||
uint64_t get_timestamp_ticks() const
|
||||
{
|
||||
return timestamp_ticks;
|
||||
}
|
||||
|
||||
bool is_signalled() const
|
||||
{
|
||||
return has_timestamp;
|
||||
}
|
||||
|
||||
bool is_device_timebase() const
|
||||
{
|
||||
return device_timebase;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Util::ObjectPool<QueryPoolResult>;
|
||||
|
||||
explicit QueryPoolResult(Device *device_, bool device_timebase_)
|
||||
: device(device_), device_timebase(device_timebase_)
|
||||
{}
|
||||
|
||||
Device *device;
|
||||
uint64_t timestamp_ticks = 0;
|
||||
bool has_timestamp = false;
|
||||
bool device_timebase = false;
|
||||
};
|
||||
|
||||
using QueryPoolHandle = Util::IntrusivePtr<QueryPoolResult>;
|
||||
|
||||
class QueryPool
|
||||
{
|
||||
public:
|
||||
explicit QueryPool(Device *device);
|
||||
|
||||
~QueryPool();
|
||||
|
||||
void begin();
|
||||
|
||||
QueryPoolHandle write_timestamp(VkCommandBuffer cmd, VkPipelineStageFlags2 stage);
|
||||
|
||||
private:
|
||||
Device *device;
|
||||
const VolkDeviceTable &table;
|
||||
|
||||
struct Pool
|
||||
{
|
||||
VkQueryPool pool = VK_NULL_HANDLE;
|
||||
std::vector<uint64_t> query_results;
|
||||
std::vector<QueryPoolHandle> cookies;
|
||||
unsigned index = 0;
|
||||
unsigned size = 0;
|
||||
};
|
||||
std::vector<Pool> pools;
|
||||
unsigned pool_index = 0;
|
||||
|
||||
void add_pool();
|
||||
|
||||
bool supports_timestamp = false;
|
||||
};
|
||||
|
||||
class TimestampInterval : public Util::IntrusiveHashMapEnabled<TimestampInterval>
|
||||
{
|
||||
public:
|
||||
explicit TimestampInterval(std::string tag);
|
||||
|
||||
void accumulate_time(double t);
|
||||
double get_time_per_iteration() const;
|
||||
double get_time_per_accumulation() const;
|
||||
const std::string &get_tag() const;
|
||||
void mark_end_of_frame_context();
|
||||
|
||||
double get_total_time() const;
|
||||
uint64_t get_total_frame_iterations() const;
|
||||
uint64_t get_total_accumulations() const;
|
||||
void reset();
|
||||
|
||||
private:
|
||||
std::string tag;
|
||||
double total_time = 0.0;
|
||||
uint64_t total_frame_iterations = 0;
|
||||
uint64_t total_accumulations = 0;
|
||||
};
|
||||
|
||||
struct TimestampIntervalReport
|
||||
{
|
||||
double time_per_accumulation;
|
||||
double time_per_frame_context;
|
||||
double accumulations_per_frame_context;
|
||||
};
|
||||
|
||||
using TimestampIntervalReportCallback = std::function<void (const std::string &, const TimestampIntervalReport &)>;
|
||||
|
||||
class TimestampIntervalManager
|
||||
{
|
||||
public:
|
||||
TimestampInterval *get_timestamp_tag(const char *tag);
|
||||
void mark_end_of_frame_context();
|
||||
void reset();
|
||||
void log_simple(const TimestampIntervalReportCallback &func = {}) const;
|
||||
|
||||
private:
|
||||
Util::IntrusiveHashMap<TimestampInterval> timestamps;
|
||||
};
|
||||
}
|
||||
51
external/parallel-rdp/parallel-rdp-standalone/vulkan/quirks.hpp
vendored
Normal file
51
external/parallel-rdp/parallel-rdp-standalone/vulkan/quirks.hpp
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
struct ImplementationQuirks
|
||||
{
|
||||
bool instance_deferred_lights = true;
|
||||
bool merge_subpasses = true;
|
||||
bool use_transient_color = true;
|
||||
bool use_transient_depth_stencil = true;
|
||||
bool queue_wait_on_submission = false;
|
||||
bool use_async_compute_post = true;
|
||||
bool render_graph_force_single_queue = false;
|
||||
bool force_no_subgroups = false;
|
||||
bool force_no_subgroup_shuffle = false;
|
||||
bool force_no_subgroup_size_control = false;
|
||||
|
||||
static ImplementationQuirks &get();
|
||||
};
|
||||
|
||||
struct ImplementationWorkarounds
|
||||
{
|
||||
bool emulate_event_as_pipeline_barrier = false;
|
||||
bool broken_pipeline_cache_control = false;
|
||||
bool force_host_cached = false;
|
||||
bool force_sync1_access = false;
|
||||
bool broken_push_descriptors = false;
|
||||
};
|
||||
}
|
||||
1031
external/parallel-rdp/parallel-rdp-standalone/vulkan/render_pass.cpp
vendored
Normal file
1031
external/parallel-rdp/parallel-rdp-standalone/vulkan/render_pass.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
270
external/parallel-rdp/parallel-rdp-standalone/vulkan/render_pass.hpp
vendored
Normal file
270
external/parallel-rdp/parallel-rdp-standalone/vulkan/render_pass.hpp
vendored
Normal file
@@ -0,0 +1,270 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cookie.hpp"
|
||||
#include "hash.hpp"
|
||||
#include "image.hpp"
|
||||
#include "intrusive.hpp"
|
||||
#include "limits.hpp"
|
||||
#include "object_pool.hpp"
|
||||
#include "temporary_hashmap.hpp"
|
||||
#include "vulkan_headers.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
enum RenderPassOp
|
||||
{
|
||||
RENDER_PASS_OP_CLEAR_DEPTH_STENCIL_BIT = 1 << 0,
|
||||
RENDER_PASS_OP_LOAD_DEPTH_STENCIL_BIT = 1 << 1,
|
||||
RENDER_PASS_OP_STORE_DEPTH_STENCIL_BIT = 1 << 2,
|
||||
RENDER_PASS_OP_DEPTH_STENCIL_READ_ONLY_BIT = 1 << 3,
|
||||
RENDER_PASS_OP_ENABLE_TRANSIENT_STORE_BIT = 1 << 4,
|
||||
RENDER_PASS_OP_ENABLE_TRANSIENT_LOAD_BIT = 1 << 5,
|
||||
RENDER_PASS_OP_PRESERVE_DEPTH_STENCIL_BIT = 1 << 6
|
||||
};
|
||||
using RenderPassOpFlags = uint32_t;
|
||||
|
||||
class ImageView;
|
||||
struct RenderPassInfo
|
||||
{
|
||||
const ImageView *color_attachments[VULKAN_NUM_ATTACHMENTS];
|
||||
const ImageView *depth_stencil = nullptr;
|
||||
unsigned num_color_attachments = 0;
|
||||
RenderPassOpFlags op_flags = 0;
|
||||
uint32_t clear_attachments = 0;
|
||||
uint32_t load_attachments = 0;
|
||||
uint32_t store_attachments = 0;
|
||||
uint32_t base_layer = 0;
|
||||
uint32_t num_layers = 1;
|
||||
|
||||
// Render area will be clipped to the actual framebuffer.
|
||||
VkRect2D render_area = { { 0, 0 }, { UINT32_MAX, UINT32_MAX } };
|
||||
|
||||
VkClearColorValue clear_color[VULKAN_NUM_ATTACHMENTS] = {};
|
||||
VkClearDepthStencilValue clear_depth_stencil = { 1.0f, 0 };
|
||||
|
||||
enum class DepthStencil
|
||||
{
|
||||
None,
|
||||
ReadOnly,
|
||||
ReadWrite
|
||||
};
|
||||
|
||||
struct Subpass
|
||||
{
|
||||
uint32_t color_attachments[VULKAN_NUM_ATTACHMENTS];
|
||||
uint32_t input_attachments[VULKAN_NUM_ATTACHMENTS];
|
||||
uint32_t resolve_attachments[VULKAN_NUM_ATTACHMENTS];
|
||||
unsigned num_color_attachments = 0;
|
||||
unsigned num_input_attachments = 0;
|
||||
unsigned num_resolve_attachments = 0;
|
||||
DepthStencil depth_stencil_mode = DepthStencil::ReadWrite;
|
||||
};
|
||||
// If 0/nullptr, assume a default subpass.
|
||||
const Subpass *subpasses = nullptr;
|
||||
unsigned num_subpasses = 0;
|
||||
};
|
||||
|
||||
class RenderPass : public HashedObject<RenderPass>, public NoCopyNoMove
|
||||
{
|
||||
public:
|
||||
struct SubpassInfo
|
||||
{
|
||||
VkAttachmentReference2 color_attachments[VULKAN_NUM_ATTACHMENTS];
|
||||
unsigned num_color_attachments;
|
||||
VkAttachmentReference2 input_attachments[VULKAN_NUM_ATTACHMENTS];
|
||||
unsigned num_input_attachments;
|
||||
VkAttachmentReference2 depth_stencil_attachment;
|
||||
|
||||
unsigned samples;
|
||||
};
|
||||
|
||||
RenderPass(Util::Hash hash, Device *device, const RenderPassInfo &info);
|
||||
RenderPass(Util::Hash hash, Device *device, const VkRenderPassCreateInfo2 &create_info);
|
||||
~RenderPass();
|
||||
|
||||
unsigned get_num_subpasses() const
|
||||
{
|
||||
return unsigned(subpasses_info.size());
|
||||
}
|
||||
|
||||
VkRenderPass get_render_pass() const
|
||||
{
|
||||
return render_pass;
|
||||
}
|
||||
|
||||
uint32_t get_sample_count(unsigned subpass) const
|
||||
{
|
||||
VK_ASSERT(subpass < subpasses_info.size());
|
||||
return subpasses_info[subpass].samples;
|
||||
}
|
||||
|
||||
unsigned get_num_color_attachments(unsigned subpass) const
|
||||
{
|
||||
VK_ASSERT(subpass < subpasses_info.size());
|
||||
return subpasses_info[subpass].num_color_attachments;
|
||||
}
|
||||
|
||||
unsigned get_num_input_attachments(unsigned subpass) const
|
||||
{
|
||||
VK_ASSERT(subpass < subpasses_info.size());
|
||||
return subpasses_info[subpass].num_input_attachments;
|
||||
}
|
||||
|
||||
const VkAttachmentReference2 &get_color_attachment(unsigned subpass, unsigned index) const
|
||||
{
|
||||
VK_ASSERT(subpass < subpasses_info.size());
|
||||
VK_ASSERT(index < subpasses_info[subpass].num_color_attachments);
|
||||
return subpasses_info[subpass].color_attachments[index];
|
||||
}
|
||||
|
||||
const VkAttachmentReference2 &get_input_attachment(unsigned subpass, unsigned index) const
|
||||
{
|
||||
VK_ASSERT(subpass < subpasses_info.size());
|
||||
VK_ASSERT(index < subpasses_info[subpass].num_input_attachments);
|
||||
return subpasses_info[subpass].input_attachments[index];
|
||||
}
|
||||
|
||||
bool has_depth(unsigned subpass) const
|
||||
{
|
||||
VK_ASSERT(subpass < subpasses_info.size());
|
||||
return subpasses_info[subpass].depth_stencil_attachment.attachment != VK_ATTACHMENT_UNUSED &&
|
||||
format_has_depth_aspect(depth_stencil);
|
||||
}
|
||||
|
||||
bool has_stencil(unsigned subpass) const
|
||||
{
|
||||
VK_ASSERT(subpass < subpasses_info.size());
|
||||
return subpasses_info[subpass].depth_stencil_attachment.attachment != VK_ATTACHMENT_UNUSED &&
|
||||
format_has_stencil_aspect(depth_stencil);
|
||||
}
|
||||
|
||||
private:
|
||||
Device *device;
|
||||
VkRenderPass render_pass = VK_NULL_HANDLE;
|
||||
|
||||
VkFormat color_attachments[VULKAN_NUM_ATTACHMENTS] = {};
|
||||
VkFormat depth_stencil = VK_FORMAT_UNDEFINED;
|
||||
std::vector<SubpassInfo> subpasses_info;
|
||||
|
||||
void setup_subpasses(const VkRenderPassCreateInfo2 &create_info);
|
||||
};
|
||||
|
||||
class Framebuffer : public Cookie, public NoCopyNoMove, public InternalSyncEnabled
|
||||
{
|
||||
public:
|
||||
Framebuffer(Device *device, const RenderPass &rp, const RenderPassInfo &info);
|
||||
~Framebuffer();
|
||||
|
||||
VkFramebuffer get_framebuffer() const
|
||||
{
|
||||
return framebuffer;
|
||||
}
|
||||
|
||||
static unsigned setup_raw_views(VkImageView *views, const RenderPassInfo &info);
|
||||
static void compute_dimensions(const RenderPassInfo &info, uint32_t &width, uint32_t &height);
|
||||
static void compute_attachment_dimensions(const RenderPassInfo &info, unsigned index, uint32_t &width, uint32_t &height);
|
||||
|
||||
uint32_t get_width() const
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
uint32_t get_height() const
|
||||
{
|
||||
return height;
|
||||
}
|
||||
|
||||
const RenderPass &get_compatible_render_pass() const
|
||||
{
|
||||
return render_pass;
|
||||
}
|
||||
|
||||
private:
|
||||
Device *device;
|
||||
VkFramebuffer framebuffer = VK_NULL_HANDLE;
|
||||
const RenderPass &render_pass;
|
||||
RenderPassInfo info;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
};
|
||||
|
||||
static const unsigned VULKAN_FRAMEBUFFER_RING_SIZE = 16;
|
||||
class FramebufferAllocator
|
||||
{
|
||||
public:
|
||||
explicit FramebufferAllocator(Device *device);
|
||||
Framebuffer &request_framebuffer(const RenderPassInfo &info);
|
||||
|
||||
void begin_frame();
|
||||
void clear();
|
||||
|
||||
private:
|
||||
struct FramebufferNode : Util::TemporaryHashmapEnabled<FramebufferNode>,
|
||||
Util::IntrusiveListEnabled<FramebufferNode>,
|
||||
Framebuffer
|
||||
{
|
||||
FramebufferNode(Device *device_, const RenderPass &rp, const RenderPassInfo &info_)
|
||||
: Framebuffer(device_, rp, info_)
|
||||
{
|
||||
set_internal_sync_object();
|
||||
}
|
||||
};
|
||||
|
||||
Device *device;
|
||||
Util::TemporaryHashmap<FramebufferNode, VULKAN_FRAMEBUFFER_RING_SIZE, false> framebuffers;
|
||||
std::mutex lock;
|
||||
};
|
||||
|
||||
class TransientAttachmentAllocator
|
||||
{
|
||||
public:
|
||||
TransientAttachmentAllocator(Device *device_)
|
||||
: device(device_)
|
||||
{
|
||||
}
|
||||
|
||||
ImageHandle request_attachment(unsigned width, unsigned height, VkFormat format,
|
||||
unsigned index = 0, unsigned samples = 1, unsigned layers = 1);
|
||||
|
||||
void begin_frame();
|
||||
void clear();
|
||||
|
||||
private:
|
||||
struct TransientNode : Util::TemporaryHashmapEnabled<TransientNode>, Util::IntrusiveListEnabled<TransientNode>
|
||||
{
|
||||
explicit TransientNode(ImageHandle handle_)
|
||||
: handle(std::move(handle_))
|
||||
{
|
||||
}
|
||||
|
||||
ImageHandle handle;
|
||||
};
|
||||
|
||||
Device *device;
|
||||
Util::TemporaryHashmap<TransientNode, VULKAN_FRAMEBUFFER_RING_SIZE, false> attachments;
|
||||
std::mutex lock;
|
||||
};
|
||||
}
|
||||
|
||||
127
external/parallel-rdp/parallel-rdp-standalone/vulkan/renderdoc_capture.cpp
vendored
Normal file
127
external/parallel-rdp/parallel-rdp-standalone/vulkan/renderdoc_capture.cpp
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "device.hpp"
|
||||
#include "renderdoc_app.h"
|
||||
#include <mutex>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
static std::mutex module_lock;
|
||||
#ifdef _WIN32
|
||||
static HMODULE renderdoc_module;
|
||||
#else
|
||||
static void *renderdoc_module;
|
||||
#endif
|
||||
|
||||
static RENDERDOC_API_1_0_0 *renderdoc_api;
|
||||
|
||||
bool Device::init_renderdoc_capture()
|
||||
{
|
||||
std::lock_guard<std::mutex> holder{module_lock};
|
||||
if (renderdoc_module)
|
||||
return true;
|
||||
|
||||
#ifdef _WIN32
|
||||
renderdoc_module = GetModuleHandleA("renderdoc.dll");
|
||||
#elif defined(ANDROID)
|
||||
renderdoc_module = dlopen("libVkLayer_GLES_RenderDoc.so", RTLD_NOW | RTLD_NOLOAD);
|
||||
#else
|
||||
renderdoc_module = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD);
|
||||
#endif
|
||||
|
||||
if (!renderdoc_module)
|
||||
{
|
||||
LOGE("Failed to load RenderDoc, make sure RenderDoc started the application in capture mode.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Workaround GCC warning about FARPROC mismatch.
|
||||
auto *gpa = GetProcAddress(renderdoc_module, "RENDERDOC_GetAPI");
|
||||
pRENDERDOC_GetAPI func;
|
||||
memcpy(&func, &gpa, sizeof(func));
|
||||
|
||||
if (!func)
|
||||
{
|
||||
LOGE("Failed to load RENDERDOC_GetAPI function.\n");
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
auto *func = reinterpret_cast<pRENDERDOC_GetAPI>(dlsym(renderdoc_module, "RENDERDOC_GetAPI"));
|
||||
if (!func)
|
||||
{
|
||||
LOGE("Failed to load RENDERDOC_GetAPI function.\n");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!func(eRENDERDOC_API_Version_1_0_0, reinterpret_cast<void **>(&renderdoc_api)))
|
||||
{
|
||||
LOGE("Failed to obtain RenderDoc 1.0.0 API.\n");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
int major, minor, patch;
|
||||
renderdoc_api->GetAPIVersion(&major, &minor, &patch);
|
||||
LOGI("Initialized RenderDoc API %d.%d.%d.\n", major, minor, patch);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Device::begin_renderdoc_capture()
|
||||
{
|
||||
std::lock_guard<std::mutex> holder{module_lock};
|
||||
if (!renderdoc_api)
|
||||
{
|
||||
LOGE("RenderDoc API is not loaded, cannot trigger capture.\n");
|
||||
return;
|
||||
}
|
||||
next_frame_context();
|
||||
|
||||
LOGI("Starting RenderDoc frame capture.\n");
|
||||
renderdoc_api->StartFrameCapture(nullptr, nullptr);
|
||||
}
|
||||
|
||||
void Device::end_renderdoc_capture()
|
||||
{
|
||||
std::lock_guard<std::mutex> holder{module_lock};
|
||||
if (!renderdoc_api)
|
||||
{
|
||||
LOGE("RenderDoc API is not loaded, cannot trigger capture.\n");
|
||||
return;
|
||||
}
|
||||
next_frame_context();
|
||||
renderdoc_api->EndFrameCapture(nullptr, nullptr);
|
||||
LOGI("Ended RenderDoc frame capture.\n");
|
||||
}
|
||||
|
||||
}
|
||||
154
external/parallel-rdp/parallel-rdp-standalone/vulkan/sampler.cpp
vendored
Normal file
154
external/parallel-rdp/parallel-rdp-standalone/vulkan/sampler.cpp
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "sampler.hpp"
|
||||
#include "device.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
Sampler::Sampler(Device *device_, VkSampler sampler_, const SamplerCreateInfo &info, bool immutable_)
|
||||
: Cookie(device_)
|
||||
, device(device_)
|
||||
, sampler(sampler_)
|
||||
, create_info(info)
|
||||
, immutable(immutable_)
|
||||
{
|
||||
}
|
||||
|
||||
Sampler::~Sampler()
|
||||
{
|
||||
if (sampler)
|
||||
{
|
||||
if (immutable)
|
||||
device->get_device_table().vkDestroySampler(device->get_device(), sampler, nullptr);
|
||||
else if (internal_sync)
|
||||
device->destroy_sampler_nolock(sampler);
|
||||
else
|
||||
device->destroy_sampler(sampler);
|
||||
}
|
||||
}
|
||||
|
||||
void SamplerDeleter::operator()(Sampler *sampler)
|
||||
{
|
||||
sampler->device->handle_pool.samplers.free(sampler);
|
||||
}
|
||||
|
||||
SamplerCreateInfo Sampler::fill_sampler_info(const VkSamplerCreateInfo &info)
|
||||
{
|
||||
SamplerCreateInfo sampler_info = {};
|
||||
|
||||
sampler_info.mag_filter = info.magFilter;
|
||||
sampler_info.min_filter = info.minFilter;
|
||||
sampler_info.mipmap_mode = info.mipmapMode;
|
||||
sampler_info.address_mode_u = info.addressModeU;
|
||||
sampler_info.address_mode_v = info.addressModeV;
|
||||
sampler_info.address_mode_w = info.addressModeW;
|
||||
sampler_info.mip_lod_bias = info.mipLodBias;
|
||||
sampler_info.anisotropy_enable = info.anisotropyEnable;
|
||||
sampler_info.max_anisotropy = info.maxAnisotropy;
|
||||
sampler_info.compare_enable = info.compareEnable;
|
||||
sampler_info.compare_op = info.compareOp;
|
||||
sampler_info.min_lod = info.minLod;
|
||||
sampler_info.max_lod = info.maxLod;
|
||||
sampler_info.border_color = info.borderColor;
|
||||
sampler_info.unnormalized_coordinates = info.unnormalizedCoordinates;
|
||||
return sampler_info;
|
||||
}
|
||||
|
||||
VkSamplerCreateInfo Sampler::fill_vk_sampler_info(const SamplerCreateInfo &sampler_info)
|
||||
{
|
||||
VkSamplerCreateInfo info = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
|
||||
|
||||
info.magFilter = sampler_info.mag_filter;
|
||||
info.minFilter = sampler_info.min_filter;
|
||||
info.mipmapMode = sampler_info.mipmap_mode;
|
||||
info.addressModeU = sampler_info.address_mode_u;
|
||||
info.addressModeV = sampler_info.address_mode_v;
|
||||
info.addressModeW = sampler_info.address_mode_w;
|
||||
info.mipLodBias = sampler_info.mip_lod_bias;
|
||||
info.anisotropyEnable = sampler_info.anisotropy_enable;
|
||||
info.maxAnisotropy = sampler_info.max_anisotropy;
|
||||
info.compareEnable = sampler_info.compare_enable;
|
||||
info.compareOp = sampler_info.compare_op;
|
||||
info.minLod = sampler_info.min_lod;
|
||||
info.maxLod = sampler_info.max_lod;
|
||||
info.borderColor = sampler_info.border_color;
|
||||
info.unnormalizedCoordinates = sampler_info.unnormalized_coordinates;
|
||||
return info;
|
||||
}
|
||||
|
||||
ImmutableSampler::ImmutableSampler(Util::Hash hash, Device *device_, const SamplerCreateInfo &sampler_info,
|
||||
const ImmutableYcbcrConversion *ycbcr_)
|
||||
: HashedObject<ImmutableSampler>(hash), device(device_), ycbcr(ycbcr_)
|
||||
{
|
||||
VkSamplerYcbcrConversionInfo conv_info = { VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO };
|
||||
auto info = Sampler::fill_vk_sampler_info(sampler_info);
|
||||
|
||||
if (ycbcr)
|
||||
{
|
||||
conv_info.conversion = ycbcr->get_conversion();
|
||||
info.pNext = &conv_info;
|
||||
}
|
||||
|
||||
#ifdef VULKAN_DEBUG
|
||||
LOGI("Creating immutable sampler.\n");
|
||||
#endif
|
||||
|
||||
VkSampler vk_sampler = VK_NULL_HANDLE;
|
||||
if (device->get_device_table().vkCreateSampler(device->get_device(), &info, nullptr, &vk_sampler) != VK_SUCCESS)
|
||||
LOGE("Failed to create sampler.\n");
|
||||
|
||||
#ifdef GRANITE_VULKAN_FOSSILIZE
|
||||
device->register_sampler(vk_sampler, hash, info);
|
||||
#endif
|
||||
|
||||
sampler = SamplerHandle(device->handle_pool.samplers.allocate(device, vk_sampler, sampler_info, true));
|
||||
}
|
||||
|
||||
ImmutableYcbcrConversion::ImmutableYcbcrConversion(Util::Hash hash, Device *device_,
|
||||
const VkSamplerYcbcrConversionCreateInfo &info)
|
||||
: HashedObject<ImmutableYcbcrConversion>(hash), device(device_)
|
||||
{
|
||||
if (device->get_device_features().vk11_features.samplerYcbcrConversion)
|
||||
{
|
||||
if (device->get_device_table().vkCreateSamplerYcbcrConversion(device->get_device(), &info, nullptr,
|
||||
&conversion) != VK_SUCCESS)
|
||||
{
|
||||
LOGE("Failed to create YCbCr conversion.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef GRANITE_VULKAN_FOSSILIZE
|
||||
device->register_sampler_ycbcr_conversion(conversion, info);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
LOGE("Ycbcr conversion is not supported on this device.\n");
|
||||
}
|
||||
|
||||
ImmutableYcbcrConversion::~ImmutableYcbcrConversion()
|
||||
{
|
||||
if (conversion)
|
||||
device->get_device_table().vkDestroySamplerYcbcrConversion(device->get_device(), conversion, nullptr);
|
||||
}
|
||||
}
|
||||
146
external/parallel-rdp/parallel-rdp-standalone/vulkan/sampler.hpp
vendored
Normal file
146
external/parallel-rdp/parallel-rdp-standalone/vulkan/sampler.hpp
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cookie.hpp"
|
||||
#include "vulkan_common.hpp"
|
||||
#include "vulkan_headers.hpp"
|
||||
#include "object_pool.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
enum class StockSampler
|
||||
{
|
||||
NearestClamp,
|
||||
LinearClamp,
|
||||
TrilinearClamp,
|
||||
NearestWrap,
|
||||
LinearWrap,
|
||||
TrilinearWrap,
|
||||
NearestShadow,
|
||||
LinearShadow,
|
||||
DefaultGeometryFilterClamp,
|
||||
DefaultGeometryFilterWrap,
|
||||
Count
|
||||
};
|
||||
|
||||
struct SamplerCreateInfo
|
||||
{
|
||||
VkFilter mag_filter;
|
||||
VkFilter min_filter;
|
||||
VkSamplerMipmapMode mipmap_mode;
|
||||
VkSamplerAddressMode address_mode_u;
|
||||
VkSamplerAddressMode address_mode_v;
|
||||
VkSamplerAddressMode address_mode_w;
|
||||
float mip_lod_bias;
|
||||
VkBool32 anisotropy_enable;
|
||||
float max_anisotropy;
|
||||
VkBool32 compare_enable;
|
||||
VkCompareOp compare_op;
|
||||
float min_lod;
|
||||
float max_lod;
|
||||
VkBorderColor border_color;
|
||||
VkBool32 unnormalized_coordinates;
|
||||
};
|
||||
|
||||
class Sampler;
|
||||
struct SamplerDeleter
|
||||
{
|
||||
void operator()(Sampler *sampler);
|
||||
};
|
||||
|
||||
class Sampler : public Util::IntrusivePtrEnabled<Sampler, SamplerDeleter, HandleCounter>,
|
||||
public Cookie, public InternalSyncEnabled
|
||||
{
|
||||
public:
|
||||
friend struct SamplerDeleter;
|
||||
~Sampler();
|
||||
|
||||
VkSampler get_sampler() const
|
||||
{
|
||||
return sampler;
|
||||
}
|
||||
|
||||
const SamplerCreateInfo &get_create_info() const
|
||||
{
|
||||
return create_info;
|
||||
}
|
||||
|
||||
static VkSamplerCreateInfo fill_vk_sampler_info(const SamplerCreateInfo &sampler_info);
|
||||
static SamplerCreateInfo fill_sampler_info(const VkSamplerCreateInfo &sampler_info);
|
||||
|
||||
private:
|
||||
friend class Util::ObjectPool<Sampler>;
|
||||
Sampler(Device *device, VkSampler sampler, const SamplerCreateInfo &info, bool immutable);
|
||||
|
||||
Device *device;
|
||||
VkSampler sampler;
|
||||
SamplerCreateInfo create_info;
|
||||
bool immutable;
|
||||
};
|
||||
using SamplerHandle = Util::IntrusivePtr<Sampler>;
|
||||
|
||||
class ImmutableYcbcrConversion : public HashedObject<ImmutableYcbcrConversion>
|
||||
{
|
||||
public:
|
||||
ImmutableYcbcrConversion(Util::Hash hash, Device *device,
|
||||
const VkSamplerYcbcrConversionCreateInfo &info);
|
||||
~ImmutableYcbcrConversion();
|
||||
void operator=(const ImmutableYcbcrConversion &) = delete;
|
||||
ImmutableYcbcrConversion(const ImmutableYcbcrConversion &) = delete;
|
||||
|
||||
VkSamplerYcbcrConversion get_conversion() const
|
||||
{
|
||||
return conversion;
|
||||
}
|
||||
|
||||
private:
|
||||
Device *device;
|
||||
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
class ImmutableSampler : public HashedObject<ImmutableSampler>
|
||||
{
|
||||
public:
|
||||
ImmutableSampler(Util::Hash hash, Device *device,
|
||||
const SamplerCreateInfo &info,
|
||||
const ImmutableYcbcrConversion *ycbcr);
|
||||
void operator=(const ImmutableSampler &) = delete;
|
||||
ImmutableSampler(const ImmutableSampler &) = delete;
|
||||
|
||||
const Sampler &get_sampler() const
|
||||
{
|
||||
return *sampler;
|
||||
}
|
||||
|
||||
VkSamplerYcbcrConversion get_ycbcr_conversion() const
|
||||
{
|
||||
return ycbcr ? ycbcr->get_conversion() : VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
private:
|
||||
Device *device;
|
||||
const ImmutableYcbcrConversion *ycbcr;
|
||||
SamplerHandle sampler;
|
||||
};
|
||||
}
|
||||
244
external/parallel-rdp/parallel-rdp-standalone/vulkan/semaphore.cpp
vendored
Normal file
244
external/parallel-rdp/parallel-rdp-standalone/vulkan/semaphore.cpp
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "semaphore.hpp"
|
||||
#include "device.hpp"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
SemaphoreHolder::~SemaphoreHolder()
|
||||
{
|
||||
recycle_semaphore();
|
||||
}
|
||||
|
||||
void SemaphoreHolder::recycle_semaphore()
|
||||
{
|
||||
if (!owned)
|
||||
return;
|
||||
|
||||
VK_ASSERT(semaphore);
|
||||
|
||||
if (internal_sync)
|
||||
{
|
||||
if (semaphore_type == VK_SEMAPHORE_TYPE_TIMELINE || external_compatible_features)
|
||||
{
|
||||
device->destroy_semaphore_nolock(semaphore);
|
||||
}
|
||||
else if (is_signalled())
|
||||
{
|
||||
// We can't just destroy a semaphore if we don't know who signals it (e.g. WSI).
|
||||
// Have to consume it by waiting then recycle.
|
||||
if (signal_is_foreign_queue)
|
||||
device->consume_semaphore_nolock(semaphore);
|
||||
else
|
||||
device->destroy_semaphore_nolock(semaphore);
|
||||
}
|
||||
else
|
||||
device->recycle_semaphore_nolock(semaphore);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (semaphore_type == VK_SEMAPHORE_TYPE_TIMELINE || external_compatible_features)
|
||||
{
|
||||
device->destroy_semaphore(semaphore);
|
||||
}
|
||||
else if (is_signalled())
|
||||
{
|
||||
// We can't just destroy a semaphore if we don't know who signals it (e.g. WSI).
|
||||
// Have to consume it by waiting then recycle.
|
||||
if (signal_is_foreign_queue)
|
||||
device->consume_semaphore(semaphore);
|
||||
else
|
||||
device->destroy_semaphore(semaphore);
|
||||
}
|
||||
else
|
||||
device->recycle_semaphore(semaphore);
|
||||
}
|
||||
}
|
||||
|
||||
bool SemaphoreHolder::wait_timeline_timeout(uint64_t value, uint64_t timeout)
|
||||
{
|
||||
VK_ASSERT(semaphore_type == VK_SEMAPHORE_TYPE_TIMELINE);
|
||||
VK_ASSERT(is_proxy_timeline());
|
||||
|
||||
VkSemaphoreWaitInfo wait_info = { VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO };
|
||||
wait_info.pSemaphores = &semaphore;
|
||||
wait_info.semaphoreCount = 1;
|
||||
wait_info.pValues = &value;
|
||||
return device->get_device_table().vkWaitSemaphores(device->get_device(), &wait_info, timeout) == VK_SUCCESS;
|
||||
}
|
||||
|
||||
void SemaphoreHolder::wait_timeline(uint64_t value)
|
||||
{
|
||||
wait_timeline_timeout(value, UINT64_MAX);
|
||||
}
|
||||
|
||||
SemaphoreHolder &SemaphoreHolder::operator=(SemaphoreHolder &&other) noexcept
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
assert(device == other.device);
|
||||
recycle_semaphore();
|
||||
|
||||
semaphore = other.semaphore;
|
||||
timeline = other.timeline;
|
||||
signalled = other.signalled;
|
||||
pending_wait = other.pending_wait;
|
||||
semaphore_type = other.semaphore_type;
|
||||
owned = other.owned;
|
||||
|
||||
other.semaphore = VK_NULL_HANDLE;
|
||||
other.timeline = 0;
|
||||
other.signalled = false;
|
||||
other.pending_wait = false;
|
||||
other.owned = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ExternalHandle SemaphoreHolder::export_to_handle()
|
||||
{
|
||||
ExternalHandle h;
|
||||
|
||||
if ((external_compatible_features & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT) == 0)
|
||||
{
|
||||
LOGE("Semaphore is not export compatible.\n");
|
||||
return h;
|
||||
}
|
||||
|
||||
if (!semaphore)
|
||||
{
|
||||
LOGE("Semaphore has already been consumed.\n");
|
||||
return h;
|
||||
}
|
||||
|
||||
// Technically we can export early with reference transference, but it's a bit dubious.
|
||||
// We want to remain compatible with copy transference for later, e.g. SYNC_FD.
|
||||
if (!signalled && semaphore_type == VK_SEMAPHORE_TYPE_BINARY)
|
||||
{
|
||||
LOGE("Cannot export payload from a semaphore that is not queued up for signal.\n");
|
||||
return h;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
VkSemaphoreGetWin32HandleInfoKHR handle_info = { VK_STRUCTURE_TYPE_SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR };
|
||||
handle_info.semaphore = semaphore;
|
||||
handle_info.handleType = external_compatible_handle_type;
|
||||
|
||||
if (device->get_device_table().vkGetSemaphoreWin32HandleKHR(device->get_device(), &handle_info, &h.handle) != VK_SUCCESS)
|
||||
{
|
||||
LOGE("Failed to export to opaque handle.\n");
|
||||
h.handle = nullptr;
|
||||
}
|
||||
#else
|
||||
VkSemaphoreGetFdInfoKHR fd_info = { VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR };
|
||||
fd_info.semaphore = semaphore;
|
||||
fd_info.handleType = external_compatible_handle_type;
|
||||
|
||||
if (device->get_device_table().vkGetSemaphoreFdKHR(device->get_device(), &fd_info, &h.handle) != VK_SUCCESS)
|
||||
{
|
||||
LOGE("Failed to export to opaque FD.\n");
|
||||
h.handle = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
h.semaphore_handle_type = external_compatible_handle_type;
|
||||
return h;
|
||||
}
|
||||
|
||||
bool SemaphoreHolder::import_from_handle(ExternalHandle handle)
|
||||
{
|
||||
if ((external_compatible_features & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT) == 0)
|
||||
{
|
||||
LOGE("Semaphore is not import compatible.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!semaphore)
|
||||
{
|
||||
LOGE("Semaphore has already been consumed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (signalled)
|
||||
{
|
||||
LOGE("Cannot import payload to semaphore that is already signalled.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (handle.semaphore_handle_type != external_compatible_handle_type)
|
||||
{
|
||||
LOGE("Mismatch in semaphore handle type.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
VkImportSemaphoreWin32HandleInfoKHR import = { VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR };
|
||||
import.handle = handle.handle;
|
||||
import.semaphore = semaphore;
|
||||
import.handleType = handle.semaphore_handle_type;
|
||||
import.flags = semaphore_type == VK_SEMAPHORE_TYPE_BINARY_KHR ? VK_SEMAPHORE_IMPORT_TEMPORARY_BIT : 0;
|
||||
if (device->get_device_table().vkImportSemaphoreWin32HandleKHR(device->get_device(), &import) != VK_SUCCESS)
|
||||
{
|
||||
LOGE("Failed to import semaphore handle %p!\n", handle.handle);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
VkImportSemaphoreFdInfoKHR import = { VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR };
|
||||
import.fd = handle.handle;
|
||||
import.semaphore = semaphore;
|
||||
import.handleType = handle.semaphore_handle_type;
|
||||
import.flags = semaphore_type == VK_SEMAPHORE_TYPE_BINARY_KHR ? VK_SEMAPHORE_IMPORT_TEMPORARY_BIT : 0;
|
||||
if (device->get_device_table().vkImportSemaphoreFdKHR(device->get_device(), &import) != VK_SUCCESS)
|
||||
{
|
||||
LOGE("Failed to import semaphore FD %d!\n", handle.handle);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ExternalHandle::semaphore_handle_type_imports_by_reference(import.handleType))
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Consume the handle, since the VkSemaphore holds a reference on Win32.
|
||||
::CloseHandle(handle.handle);
|
||||
#else
|
||||
::close(handle.handle);
|
||||
#endif
|
||||
}
|
||||
|
||||
signal_external();
|
||||
return true;
|
||||
}
|
||||
|
||||
void SemaphoreHolderDeleter::operator()(Vulkan::SemaphoreHolder *semaphore)
|
||||
{
|
||||
semaphore->device->handle_pool.semaphores.free(semaphore);
|
||||
}
|
||||
}
|
||||
206
external/parallel-rdp/parallel-rdp-standalone/vulkan/semaphore.hpp
vendored
Normal file
206
external/parallel-rdp/parallel-rdp-standalone/vulkan/semaphore.hpp
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vulkan_common.hpp"
|
||||
#include "vulkan_headers.hpp"
|
||||
#include "cookie.hpp"
|
||||
#include "object_pool.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class Device;
|
||||
|
||||
class SemaphoreHolder;
|
||||
struct SemaphoreHolderDeleter
|
||||
{
|
||||
void operator()(SemaphoreHolder *semaphore);
|
||||
};
|
||||
|
||||
class SemaphoreHolder : public Util::IntrusivePtrEnabled<SemaphoreHolder, SemaphoreHolderDeleter, HandleCounter>,
|
||||
public InternalSyncEnabled
|
||||
{
|
||||
public:
|
||||
friend struct SemaphoreHolderDeleter;
|
||||
|
||||
~SemaphoreHolder();
|
||||
|
||||
const VkSemaphore &get_semaphore() const
|
||||
{
|
||||
return semaphore;
|
||||
}
|
||||
|
||||
bool is_signalled() const
|
||||
{
|
||||
return signalled;
|
||||
}
|
||||
|
||||
uint64_t get_timeline_value() const
|
||||
{
|
||||
VK_ASSERT(!owned && semaphore_type == VK_SEMAPHORE_TYPE_TIMELINE_KHR);
|
||||
return timeline;
|
||||
}
|
||||
|
||||
VkSemaphore consume()
|
||||
{
|
||||
auto ret = semaphore;
|
||||
VK_ASSERT(semaphore);
|
||||
VK_ASSERT(signalled);
|
||||
semaphore = VK_NULL_HANDLE;
|
||||
signalled = false;
|
||||
owned = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
VkSemaphore release_semaphore()
|
||||
{
|
||||
auto ret = semaphore;
|
||||
semaphore = VK_NULL_HANDLE;
|
||||
signalled = false;
|
||||
owned = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void wait_external()
|
||||
{
|
||||
VK_ASSERT(semaphore);
|
||||
VK_ASSERT(signalled);
|
||||
signalled = false;
|
||||
}
|
||||
|
||||
void signal_external()
|
||||
{
|
||||
VK_ASSERT(!signalled);
|
||||
VK_ASSERT(semaphore);
|
||||
signalled = true;
|
||||
}
|
||||
|
||||
void set_signal_is_foreign_queue()
|
||||
{
|
||||
VK_ASSERT(signalled);
|
||||
signal_is_foreign_queue = true;
|
||||
}
|
||||
|
||||
void set_pending_wait()
|
||||
{
|
||||
pending_wait = true;
|
||||
}
|
||||
|
||||
bool is_pending_wait() const
|
||||
{
|
||||
return pending_wait;
|
||||
}
|
||||
|
||||
void set_external_object_compatible(VkExternalSemaphoreHandleTypeFlagBits handle_type,
|
||||
VkExternalSemaphoreFeatureFlags features)
|
||||
{
|
||||
external_compatible_handle_type = handle_type;
|
||||
external_compatible_features = features;
|
||||
}
|
||||
|
||||
bool is_external_object_compatible() const
|
||||
{
|
||||
return external_compatible_features != 0;
|
||||
}
|
||||
|
||||
VkSemaphoreTypeKHR get_semaphore_type() const
|
||||
{
|
||||
return semaphore_type;
|
||||
}
|
||||
|
||||
bool is_proxy_timeline() const
|
||||
{
|
||||
return proxy_timeline;
|
||||
}
|
||||
|
||||
void set_proxy_timeline()
|
||||
{
|
||||
proxy_timeline = true;
|
||||
signalled = false;
|
||||
}
|
||||
|
||||
// If successful, importing takes ownership of the handle/fd.
|
||||
// Application can use dup() / DuplicateHandle() to keep a reference.
|
||||
// Imported semaphores are assumed to be signalled, or pending to be signalled.
|
||||
// All imports are performed with TEMPORARY permanence.
|
||||
ExternalHandle export_to_handle();
|
||||
bool import_from_handle(ExternalHandle handle);
|
||||
|
||||
VkExternalSemaphoreFeatureFlags get_external_features() const
|
||||
{
|
||||
return external_compatible_features;
|
||||
}
|
||||
|
||||
VkExternalSemaphoreHandleTypeFlagBits get_external_handle_type() const
|
||||
{
|
||||
return external_compatible_handle_type;
|
||||
}
|
||||
|
||||
SemaphoreHolder &operator=(SemaphoreHolder &&other) noexcept;
|
||||
|
||||
void wait_timeline(uint64_t value);
|
||||
bool wait_timeline_timeout(uint64_t value, uint64_t timeout);
|
||||
|
||||
private:
|
||||
friend class Util::ObjectPool<SemaphoreHolder>;
|
||||
SemaphoreHolder(Device *device_, VkSemaphore semaphore_, bool signalled_, bool owned_)
|
||||
: device(device_)
|
||||
, semaphore(semaphore_)
|
||||
, timeline(0)
|
||||
, semaphore_type(VK_SEMAPHORE_TYPE_BINARY_KHR)
|
||||
, signalled(signalled_)
|
||||
, owned(owned_)
|
||||
{
|
||||
}
|
||||
|
||||
SemaphoreHolder(Device *device_, uint64_t timeline_, VkSemaphore semaphore_, bool owned_)
|
||||
: device(device_)
|
||||
, semaphore(semaphore_)
|
||||
, timeline(timeline_)
|
||||
, semaphore_type(VK_SEMAPHORE_TYPE_TIMELINE_KHR)
|
||||
, owned(owned_)
|
||||
{
|
||||
}
|
||||
|
||||
explicit SemaphoreHolder(Device *device_)
|
||||
: device(device_)
|
||||
{
|
||||
}
|
||||
|
||||
void recycle_semaphore();
|
||||
|
||||
Device *device;
|
||||
VkSemaphore semaphore = VK_NULL_HANDLE;
|
||||
uint64_t timeline = 0;
|
||||
VkSemaphoreTypeKHR semaphore_type = VK_SEMAPHORE_TYPE_BINARY_KHR;
|
||||
bool signalled = false;
|
||||
bool pending_wait = false;
|
||||
bool owned = false;
|
||||
bool proxy_timeline = false;
|
||||
bool signal_is_foreign_queue = false;
|
||||
VkExternalSemaphoreHandleTypeFlagBits external_compatible_handle_type = {};
|
||||
VkExternalSemaphoreFeatureFlags external_compatible_features = 0;
|
||||
};
|
||||
|
||||
using Semaphore = Util::IntrusivePtr<SemaphoreHolder>;
|
||||
}
|
||||
68
external/parallel-rdp/parallel-rdp-standalone/vulkan/semaphore_manager.cpp
vendored
Normal file
68
external/parallel-rdp/parallel-rdp-standalone/vulkan/semaphore_manager.cpp
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "semaphore_manager.hpp"
|
||||
#include "device.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
void SemaphoreManager::init(Device *device_)
|
||||
{
|
||||
device = device_;
|
||||
table = &device->get_device_table();
|
||||
}
|
||||
|
||||
SemaphoreManager::~SemaphoreManager()
|
||||
{
|
||||
for (auto &sem : semaphores)
|
||||
table->vkDestroySemaphore(device->get_device(), sem, nullptr);
|
||||
}
|
||||
|
||||
void SemaphoreManager::recycle(VkSemaphore sem)
|
||||
{
|
||||
if (sem != VK_NULL_HANDLE)
|
||||
semaphores.push_back(sem);
|
||||
}
|
||||
|
||||
VkSemaphore SemaphoreManager::request_cleared_semaphore()
|
||||
{
|
||||
if (semaphores.empty())
|
||||
{
|
||||
VkSemaphoreCreateInfo info = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
|
||||
VkSemaphore semaphore;
|
||||
|
||||
if (table->vkCreateSemaphore(device->get_device(), &info, nullptr, &semaphore) != VK_SUCCESS)
|
||||
{
|
||||
LOGE("Failed to create semaphore.\n");
|
||||
semaphore = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return semaphore;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto sem = semaphores.back();
|
||||
semaphores.pop_back();
|
||||
return sem;
|
||||
}
|
||||
}
|
||||
}
|
||||
45
external/parallel-rdp/parallel-rdp-standalone/vulkan/semaphore_manager.hpp
vendored
Normal file
45
external/parallel-rdp/parallel-rdp-standalone/vulkan/semaphore_manager.hpp
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vulkan_headers.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class Device;
|
||||
class SemaphoreManager
|
||||
{
|
||||
public:
|
||||
void init(Device *device);
|
||||
~SemaphoreManager();
|
||||
|
||||
VkSemaphore request_cleared_semaphore();
|
||||
void recycle(VkSemaphore semaphore);
|
||||
|
||||
private:
|
||||
Device *device = nullptr;
|
||||
const VolkDeviceTable *table = nullptr;
|
||||
std::vector<VkSemaphore> semaphores;
|
||||
};
|
||||
}
|
||||
687
external/parallel-rdp/parallel-rdp-standalone/vulkan/shader.cpp
vendored
Normal file
687
external/parallel-rdp/parallel-rdp-standalone/vulkan/shader.cpp
vendored
Normal file
@@ -0,0 +1,687 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include "shader.hpp"
|
||||
#include "device.hpp"
|
||||
#ifdef GRANITE_VULKAN_SPIRV_CROSS
|
||||
#include "spirv_cross.hpp"
|
||||
using namespace spirv_cross;
|
||||
#endif
|
||||
|
||||
using namespace Util;
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
void ImmutableSamplerBank::hash(Util::Hasher &h, const ImmutableSamplerBank *sampler_bank)
|
||||
{
|
||||
h.u32(0);
|
||||
if (sampler_bank)
|
||||
{
|
||||
unsigned index = 0;
|
||||
for (auto &set : sampler_bank->samplers)
|
||||
{
|
||||
for (auto *binding : set)
|
||||
{
|
||||
if (binding)
|
||||
{
|
||||
h.u32(index);
|
||||
h.u64(binding->get_hash());
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PipelineLayout::PipelineLayout(Hash hash, Device *device_, const CombinedResourceLayout &layout_,
|
||||
const ImmutableSamplerBank *immutable_samplers)
|
||||
: IntrusiveHashMapEnabled<PipelineLayout>(hash)
|
||||
, device(device_)
|
||||
, layout(layout_)
|
||||
{
|
||||
VkDescriptorSetLayout layouts[VULKAN_NUM_DESCRIPTOR_SETS] = {};
|
||||
unsigned num_sets = 0;
|
||||
for (unsigned i = 0; i < VULKAN_NUM_DESCRIPTOR_SETS; i++)
|
||||
{
|
||||
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_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 (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)
|
||||
{
|
||||
info.setLayoutCount = num_sets;
|
||||
info.pSetLayouts = layouts;
|
||||
}
|
||||
|
||||
if (layout.push_constant_range.stageFlags != 0)
|
||||
{
|
||||
info.pushConstantRangeCount = 1;
|
||||
info.pPushConstantRanges = &layout.push_constant_range;
|
||||
}
|
||||
|
||||
#ifdef VULKAN_DEBUG
|
||||
LOGI("Creating pipeline layout.\n");
|
||||
#endif
|
||||
auto &table = device->get_device_table();
|
||||
if (table.vkCreatePipelineLayout(device->get_device(), &info, nullptr, &pipe_layout) != VK_SUCCESS)
|
||||
LOGE("Failed to create pipeline layout.\n");
|
||||
#ifdef GRANITE_VULKAN_FOSSILIZE
|
||||
device->register_pipeline_layout(pipe_layout, get_hash(), info);
|
||||
#endif
|
||||
|
||||
create_update_templates();
|
||||
}
|
||||
|
||||
void PipelineLayout::create_update_templates()
|
||||
{
|
||||
auto &table = device->get_device_table();
|
||||
for (unsigned desc_set = 0; desc_set < VULKAN_NUM_DESCRIPTOR_SETS; desc_set++)
|
||||
{
|
||||
if ((layout.descriptor_set_mask & (1u << desc_set)) == 0)
|
||||
continue;
|
||||
if ((layout.bindless_descriptor_set_mask & (1u << desc_set)) != 0)
|
||||
continue;
|
||||
|
||||
VkDescriptorUpdateTemplateEntry update_entries[VULKAN_NUM_BINDINGS];
|
||||
uint32_t update_count = 0;
|
||||
|
||||
auto &set_layout = layout.sets[desc_set];
|
||||
|
||||
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);
|
||||
// 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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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 };
|
||||
info.pipelineLayout = pipe_layout;
|
||||
|
||||
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;
|
||||
info.pipelineBindPoint = (layout.stages_for_sets[desc_set] & VK_SHADER_STAGE_COMPUTE_BIT) ?
|
||||
VK_PIPELINE_BIND_POINT_COMPUTE : VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
|
||||
if (table.vkCreateDescriptorUpdateTemplate(device->get_device(), &info, nullptr,
|
||||
&update_template[desc_set]) != VK_SUCCESS)
|
||||
{
|
||||
LOGE("Failed to create descriptor update template.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PipelineLayout::~PipelineLayout()
|
||||
{
|
||||
auto &table = device->get_device_table();
|
||||
if (pipe_layout != VK_NULL_HANDLE)
|
||||
table.vkDestroyPipelineLayout(device->get_device(), pipe_layout, nullptr);
|
||||
|
||||
for (auto &update : update_template)
|
||||
if (update != VK_NULL_HANDLE)
|
||||
table.vkDestroyDescriptorUpdateTemplate(device->get_device(), update, nullptr);
|
||||
}
|
||||
|
||||
const char *Shader::stage_to_name(ShaderStage stage)
|
||||
{
|
||||
switch (stage)
|
||||
{
|
||||
case ShaderStage::Compute:
|
||||
return "compute";
|
||||
case ShaderStage::Vertex:
|
||||
return "vertex";
|
||||
case ShaderStage::Fragment:
|
||||
return "fragment";
|
||||
case ShaderStage::Geometry:
|
||||
return "geometry";
|
||||
case ShaderStage::TessControl:
|
||||
return "tess_control";
|
||||
case ShaderStage::TessEvaluation:
|
||||
return "tess_evaluation";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// Implicitly also checks for endian issues.
|
||||
static const uint16_t reflection_magic[] = { 'G', 'R', 'A', ResourceLayout::Version };
|
||||
|
||||
size_t ResourceLayout::serialization_size()
|
||||
{
|
||||
return sizeof(ResourceLayout) + sizeof(reflection_magic);
|
||||
}
|
||||
|
||||
bool ResourceLayout::serialize(uint8_t *data, size_t size) const
|
||||
{
|
||||
if (size != serialization_size())
|
||||
return false;
|
||||
|
||||
// Cannot serialize externally defined immutable samplers.
|
||||
for (auto &set : sets)
|
||||
if (set.immutable_sampler_mask != 0)
|
||||
return false;
|
||||
|
||||
memcpy(data, reflection_magic, sizeof(reflection_magic));
|
||||
memcpy(data + sizeof(reflection_magic), this, sizeof(*this));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceLayout::unserialize(const uint8_t *data, size_t size)
|
||||
{
|
||||
if (size != sizeof(*this) + sizeof(reflection_magic))
|
||||
{
|
||||
LOGE("Reflection size mismatch.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(data, reflection_magic, sizeof(reflection_magic)) != 0)
|
||||
{
|
||||
LOGE("Magic mismatch.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(this, data + sizeof(reflection_magic), sizeof(*this));
|
||||
return true;
|
||||
}
|
||||
|
||||
Util::Hash Shader::hash(const uint32_t *data, size_t size)
|
||||
{
|
||||
Util::Hasher hasher;
|
||||
hasher.data(data, size);
|
||||
return hasher.get();
|
||||
}
|
||||
|
||||
#ifdef GRANITE_VULKAN_SPIRV_CROSS
|
||||
static void update_array_info(ResourceLayout &layout, const SPIRType &type, unsigned set, unsigned binding)
|
||||
{
|
||||
auto &size = layout.sets[set].array_size[binding];
|
||||
if (!type.array.empty())
|
||||
{
|
||||
if (type.array.size() != 1)
|
||||
LOGE("Array dimension must be 1.\n");
|
||||
else if (!type.array_size_literal.front())
|
||||
LOGE("Array dimension must be a literal.\n");
|
||||
else
|
||||
{
|
||||
if (type.array.front() == 0)
|
||||
{
|
||||
if (binding != 0)
|
||||
LOGE("Bindless textures can only be used with binding = 0 in a set.\n");
|
||||
|
||||
if (type.basetype != SPIRType::Image || type.image.dim == spv::DimBuffer)
|
||||
{
|
||||
LOGE("Can only use bindless for sampled images.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
layout.bindless_set_mask |= 1u << set;
|
||||
// Ignore fp_mask for bindless since we can mix and match.
|
||||
layout.sets[set].fp_mask = 0;
|
||||
}
|
||||
|
||||
size = DescriptorSetLayout::UNSIZED_ARRAY;
|
||||
}
|
||||
else if (size && size != type.array.front())
|
||||
LOGE("Array dimension for (%u, %u) is inconsistent.\n", set, binding);
|
||||
else if (type.array.front() + binding > VULKAN_NUM_BINDINGS)
|
||||
LOGE("Binding array will go out of bounds.\n");
|
||||
else
|
||||
size = uint8_t(type.array.front());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (size && size != 1)
|
||||
LOGE("Array dimension for (%u, %u) is inconsistent.\n", set, binding);
|
||||
size = 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool Shader::reflect_resource_layout(ResourceLayout &layout, const uint32_t *data, size_t size)
|
||||
{
|
||||
Compiler compiler(data, size / sizeof(uint32_t));
|
||||
|
||||
#ifdef VULKAN_DEBUG
|
||||
LOGI("Reflecting shader layout.\n");
|
||||
#endif
|
||||
|
||||
auto resources = compiler.get_shader_resources();
|
||||
for (auto &image : resources.sampled_images)
|
||||
{
|
||||
auto set = compiler.get_decoration(image.id, spv::DecorationDescriptorSet);
|
||||
auto binding = compiler.get_decoration(image.id, spv::DecorationBinding);
|
||||
VK_ASSERT(set < VULKAN_NUM_DESCRIPTOR_SETS);
|
||||
VK_ASSERT(binding < VULKAN_NUM_BINDINGS);
|
||||
|
||||
auto &type = compiler.get_type(image.type_id);
|
||||
if (type.image.dim == spv::DimBuffer)
|
||||
layout.sets[set].sampled_texel_buffer_mask |= 1u << binding;
|
||||
else
|
||||
layout.sets[set].sampled_image_mask |= 1u << binding;
|
||||
|
||||
if (compiler.get_type(type.image.type).basetype == SPIRType::BaseType::Float)
|
||||
layout.sets[set].fp_mask |= 1u << binding;
|
||||
|
||||
update_array_info(layout, type, set, binding);
|
||||
}
|
||||
|
||||
for (auto &image : resources.subpass_inputs)
|
||||
{
|
||||
auto set = compiler.get_decoration(image.id, spv::DecorationDescriptorSet);
|
||||
auto binding = compiler.get_decoration(image.id, spv::DecorationBinding);
|
||||
VK_ASSERT(set < VULKAN_NUM_DESCRIPTOR_SETS);
|
||||
VK_ASSERT(binding < VULKAN_NUM_BINDINGS);
|
||||
|
||||
layout.sets[set].input_attachment_mask |= 1u << binding;
|
||||
|
||||
auto &type = compiler.get_type(image.type_id);
|
||||
if (compiler.get_type(type.image.type).basetype == SPIRType::BaseType::Float)
|
||||
layout.sets[set].fp_mask |= 1u << binding;
|
||||
update_array_info(layout, type, set, binding);
|
||||
}
|
||||
|
||||
for (auto &image : resources.separate_images)
|
||||
{
|
||||
auto set = compiler.get_decoration(image.id, spv::DecorationDescriptorSet);
|
||||
auto binding = compiler.get_decoration(image.id, spv::DecorationBinding);
|
||||
VK_ASSERT(set < VULKAN_NUM_DESCRIPTOR_SETS);
|
||||
VK_ASSERT(binding < VULKAN_NUM_BINDINGS);
|
||||
|
||||
auto &type = compiler.get_type(image.type_id);
|
||||
if (compiler.get_type(type.image.type).basetype == SPIRType::BaseType::Float)
|
||||
layout.sets[set].fp_mask |= 1u << binding;
|
||||
|
||||
if (type.image.dim == spv::DimBuffer)
|
||||
layout.sets[set].sampled_texel_buffer_mask |= 1u << binding;
|
||||
else
|
||||
layout.sets[set].separate_image_mask |= 1u << binding;
|
||||
|
||||
update_array_info(layout, type, set, binding);
|
||||
}
|
||||
|
||||
for (auto &image : resources.separate_samplers)
|
||||
{
|
||||
auto set = compiler.get_decoration(image.id, spv::DecorationDescriptorSet);
|
||||
auto binding = compiler.get_decoration(image.id, spv::DecorationBinding);
|
||||
VK_ASSERT(set < VULKAN_NUM_DESCRIPTOR_SETS);
|
||||
VK_ASSERT(binding < VULKAN_NUM_BINDINGS);
|
||||
|
||||
layout.sets[set].sampler_mask |= 1u << binding;
|
||||
update_array_info(layout, compiler.get_type(image.type_id), set, binding);
|
||||
}
|
||||
|
||||
for (auto &image : resources.storage_images)
|
||||
{
|
||||
auto set = compiler.get_decoration(image.id, spv::DecorationDescriptorSet);
|
||||
auto binding = compiler.get_decoration(image.id, spv::DecorationBinding);
|
||||
VK_ASSERT(set < VULKAN_NUM_DESCRIPTOR_SETS);
|
||||
VK_ASSERT(binding < VULKAN_NUM_BINDINGS);
|
||||
|
||||
auto &type = compiler.get_type(image.type_id);
|
||||
if (type.image.dim == spv::DimBuffer)
|
||||
layout.sets[set].storage_texel_buffer_mask |= 1u << binding;
|
||||
else
|
||||
layout.sets[set].storage_image_mask |= 1u << binding;
|
||||
|
||||
if (compiler.get_type(type.image.type).basetype == SPIRType::BaseType::Float)
|
||||
layout.sets[set].fp_mask |= 1u << binding;
|
||||
|
||||
update_array_info(layout, type, set, binding);
|
||||
}
|
||||
|
||||
for (auto &buffer : resources.uniform_buffers)
|
||||
{
|
||||
auto set = compiler.get_decoration(buffer.id, spv::DecorationDescriptorSet);
|
||||
auto binding = compiler.get_decoration(buffer.id, spv::DecorationBinding);
|
||||
VK_ASSERT(set < VULKAN_NUM_DESCRIPTOR_SETS);
|
||||
VK_ASSERT(binding < VULKAN_NUM_BINDINGS);
|
||||
|
||||
layout.sets[set].uniform_buffer_mask |= 1u << binding;
|
||||
update_array_info(layout, compiler.get_type(buffer.type_id), set, binding);
|
||||
}
|
||||
|
||||
for (auto &buffer : resources.storage_buffers)
|
||||
{
|
||||
auto set = compiler.get_decoration(buffer.id, spv::DecorationDescriptorSet);
|
||||
auto binding = compiler.get_decoration(buffer.id, spv::DecorationBinding);
|
||||
VK_ASSERT(set < VULKAN_NUM_DESCRIPTOR_SETS);
|
||||
VK_ASSERT(binding < VULKAN_NUM_BINDINGS);
|
||||
|
||||
layout.sets[set].storage_buffer_mask |= 1u << binding;
|
||||
update_array_info(layout, compiler.get_type(buffer.type_id), set, binding);
|
||||
}
|
||||
|
||||
for (auto &attrib : resources.stage_inputs)
|
||||
{
|
||||
auto location = compiler.get_decoration(attrib.id, spv::DecorationLocation);
|
||||
layout.input_mask |= 1u << location;
|
||||
}
|
||||
|
||||
for (auto &attrib : resources.stage_outputs)
|
||||
{
|
||||
auto location = compiler.get_decoration(attrib.id, spv::DecorationLocation);
|
||||
layout.output_mask |= 1u << location;
|
||||
}
|
||||
|
||||
if (!resources.push_constant_buffers.empty())
|
||||
{
|
||||
// Don't bother trying to extract which part of a push constant block we're using.
|
||||
// Just assume we're accessing everything. At least on older validation layers,
|
||||
// it did not do a static analysis to determine similar information, so we got a lot
|
||||
// of false positives.
|
||||
layout.push_constant_size =
|
||||
compiler.get_declared_struct_size(compiler.get_type(resources.push_constant_buffers.front().base_type_id));
|
||||
}
|
||||
|
||||
auto spec_constants = compiler.get_specialization_constants();
|
||||
for (auto &c : spec_constants)
|
||||
{
|
||||
if (c.constant_id >= VULKAN_NUM_TOTAL_SPEC_CONSTANTS)
|
||||
{
|
||||
LOGE("Spec constant ID: %u is out of range, will be ignored.\n", c.constant_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
layout.spec_constant_mask |= 1u << c.constant_id;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
bool Shader::reflect_resource_layout(ResourceLayout &, const uint32_t *, size_t)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
Shader::Shader(Hash hash, Device *device_, const uint32_t *data, size_t size,
|
||||
const ResourceLayout *resource_layout)
|
||||
: IntrusiveHashMapEnabled<Shader>(hash)
|
||||
, device(device_)
|
||||
{
|
||||
VkShaderModuleCreateInfo info = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
|
||||
info.codeSize = size;
|
||||
info.pCode = data;
|
||||
|
||||
#ifdef VULKAN_DEBUG
|
||||
LOGI("Creating shader module.\n");
|
||||
#endif
|
||||
auto &table = device->get_device_table();
|
||||
if (table.vkCreateShaderModule(device->get_device(), &info, nullptr, &module) != VK_SUCCESS)
|
||||
LOGE("Failed to create shader module.\n");
|
||||
|
||||
#ifdef GRANITE_VULKAN_FOSSILIZE
|
||||
device->register_shader_module(module, get_hash(), info);
|
||||
#endif
|
||||
|
||||
if (resource_layout)
|
||||
layout = *resource_layout;
|
||||
#ifdef GRANITE_VULKAN_SPIRV_CROSS
|
||||
else if (!reflect_resource_layout(layout, data, size))
|
||||
LOGE("Failed to reflect resource layout.\n");
|
||||
#endif
|
||||
|
||||
if (layout.bindless_set_mask != 0 && !device->get_device_features().vk12_features.descriptorIndexing)
|
||||
LOGE("Sufficient features for descriptor indexing is not supported on this device.\n");
|
||||
}
|
||||
|
||||
Shader::~Shader()
|
||||
{
|
||||
auto &table = device->get_device_table();
|
||||
if (module)
|
||||
table.vkDestroyShaderModule(device->get_device(), module, nullptr);
|
||||
}
|
||||
|
||||
void Program::set_shader(ShaderStage stage, Shader *handle)
|
||||
{
|
||||
shaders[Util::ecast(stage)] = handle;
|
||||
}
|
||||
|
||||
Program::Program(Device *device_, Shader *vertex, Shader *fragment, const ImmutableSamplerBank *sampler_bank)
|
||||
: device(device_)
|
||||
{
|
||||
set_shader(ShaderStage::Vertex, vertex);
|
||||
set_shader(ShaderStage::Fragment, fragment);
|
||||
device->bake_program(*this, sampler_bank);
|
||||
}
|
||||
|
||||
Program::Program(Device *device_, Shader *task, Shader *mesh, Shader *fragment, const ImmutableSamplerBank *sampler_bank)
|
||||
: device(device_)
|
||||
{
|
||||
if (task)
|
||||
set_shader(ShaderStage::Task, task);
|
||||
set_shader(ShaderStage::Mesh, mesh);
|
||||
set_shader(ShaderStage::Fragment, fragment);
|
||||
device->bake_program(*this, sampler_bank);
|
||||
}
|
||||
|
||||
Program::Program(Device *device_, Shader *compute_shader, const ImmutableSamplerBank *sampler_bank)
|
||||
: device(device_)
|
||||
{
|
||||
set_shader(ShaderStage::Compute, compute_shader);
|
||||
device->bake_program(*this, sampler_bank);
|
||||
}
|
||||
|
||||
Pipeline Program::get_pipeline(Hash hash) const
|
||||
{
|
||||
auto *ret = pipelines.find(hash);
|
||||
return ret ? ret->get() : Pipeline{};
|
||||
}
|
||||
|
||||
Pipeline Program::add_pipeline(Hash hash, const Pipeline &pipeline)
|
||||
{
|
||||
return pipelines.emplace_yield(hash, pipeline)->get();
|
||||
}
|
||||
|
||||
void Program::destroy_pipeline(const Pipeline &pipeline)
|
||||
{
|
||||
device->get_device_table().vkDestroyPipeline(device->get_device(), pipeline.pipeline, nullptr);
|
||||
}
|
||||
|
||||
void Program::promote_read_write_to_read_only()
|
||||
{
|
||||
pipelines.move_to_read_only();
|
||||
}
|
||||
|
||||
Program::~Program()
|
||||
{
|
||||
for (auto &pipe : pipelines.get_read_only())
|
||||
destroy_pipeline(pipe.get());
|
||||
for (auto &pipe : pipelines.get_read_write())
|
||||
destroy_pipeline(pipe.get());
|
||||
}
|
||||
}
|
||||
228
external/parallel-rdp/parallel-rdp-standalone/vulkan/shader.hpp
vendored
Normal file
228
external/parallel-rdp/parallel-rdp-standalone/vulkan/shader.hpp
vendored
Normal file
@@ -0,0 +1,228 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cookie.hpp"
|
||||
#include "descriptor_set.hpp"
|
||||
#include "hash.hpp"
|
||||
#include "intrusive.hpp"
|
||||
#include "limits.hpp"
|
||||
#include "vulkan_headers.hpp"
|
||||
#include "enum_cast.hpp"
|
||||
|
||||
namespace spirv_cross
|
||||
{
|
||||
struct SPIRType;
|
||||
}
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class Device;
|
||||
|
||||
enum class ShaderStage
|
||||
{
|
||||
Vertex = 0,
|
||||
TessControl = 1,
|
||||
TessEvaluation = 2,
|
||||
Geometry = 3,
|
||||
Fragment = 4,
|
||||
Compute = 5,
|
||||
Task = 6,
|
||||
Mesh = 7,
|
||||
Count
|
||||
};
|
||||
|
||||
struct ResourceLayout
|
||||
{
|
||||
DescriptorSetLayout sets[VULKAN_NUM_DESCRIPTOR_SETS];
|
||||
uint32_t input_mask = 0;
|
||||
uint32_t output_mask = 0;
|
||||
uint32_t push_constant_size = 0;
|
||||
uint32_t spec_constant_mask = 0;
|
||||
uint32_t bindless_set_mask = 0;
|
||||
enum { Version = 4 };
|
||||
|
||||
bool unserialize(const uint8_t *data, size_t size);
|
||||
bool serialize(uint8_t *data, size_t size) const;
|
||||
static size_t serialization_size();
|
||||
};
|
||||
static_assert(sizeof(DescriptorSetLayout) % 8 == 0, "Size of DescriptorSetLayout does not align to 64 bits.");
|
||||
|
||||
struct CombinedResourceLayout
|
||||
{
|
||||
uint32_t attribute_mask = 0;
|
||||
uint32_t render_target_mask = 0;
|
||||
DescriptorSetLayout sets[VULKAN_NUM_DESCRIPTOR_SETS] = {};
|
||||
uint32_t stages_for_bindings[VULKAN_NUM_DESCRIPTOR_SETS][VULKAN_NUM_BINDINGS] = {};
|
||||
uint32_t stages_for_sets[VULKAN_NUM_DESCRIPTOR_SETS] = {};
|
||||
VkPushConstantRange push_constant_range = {};
|
||||
uint32_t descriptor_set_mask = 0;
|
||||
uint32_t bindless_descriptor_set_mask = 0;
|
||||
uint32_t spec_constant_mask[Util::ecast(ShaderStage::Count)] = {};
|
||||
uint32_t combined_spec_constant_mask = 0;
|
||||
Util::Hash push_constant_layout_hash = 0;
|
||||
};
|
||||
|
||||
union ResourceBinding
|
||||
{
|
||||
struct
|
||||
{
|
||||
VkDescriptorBufferInfo dynamic;
|
||||
VkDescriptorBufferInfo push;
|
||||
} buffer;
|
||||
|
||||
struct
|
||||
{
|
||||
VkDescriptorImageInfo fp;
|
||||
VkDescriptorImageInfo integer;
|
||||
} image;
|
||||
|
||||
VkBufferView buffer_view;
|
||||
};
|
||||
|
||||
struct ResourceBindings
|
||||
{
|
||||
ResourceBinding bindings[VULKAN_NUM_DESCRIPTOR_SETS][VULKAN_NUM_BINDINGS];
|
||||
uint64_t cookies[VULKAN_NUM_DESCRIPTOR_SETS][VULKAN_NUM_BINDINGS];
|
||||
uint64_t secondary_cookies[VULKAN_NUM_DESCRIPTOR_SETS][VULKAN_NUM_BINDINGS];
|
||||
uint8_t push_constant_data[VULKAN_PUSH_CONSTANT_SIZE];
|
||||
};
|
||||
|
||||
struct ImmutableSamplerBank
|
||||
{
|
||||
const ImmutableSampler *samplers[VULKAN_NUM_DESCRIPTOR_SETS][VULKAN_NUM_BINDINGS];
|
||||
static void hash(Util::Hasher &h, const ImmutableSamplerBank *bank);
|
||||
};
|
||||
|
||||
class PipelineLayout : public HashedObject<PipelineLayout>
|
||||
{
|
||||
public:
|
||||
PipelineLayout(Util::Hash hash, Device *device, const CombinedResourceLayout &layout,
|
||||
const ImmutableSamplerBank *sampler_bank);
|
||||
~PipelineLayout();
|
||||
|
||||
const CombinedResourceLayout &get_resource_layout() const
|
||||
{
|
||||
return layout;
|
||||
}
|
||||
|
||||
VkPipelineLayout get_layout() const
|
||||
{
|
||||
return pipe_layout;
|
||||
}
|
||||
|
||||
DescriptorSetAllocator *get_allocator(unsigned set) const
|
||||
{
|
||||
return set_allocators[set];
|
||||
}
|
||||
|
||||
VkDescriptorUpdateTemplate get_update_template(unsigned set) const
|
||||
{
|
||||
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();
|
||||
};
|
||||
|
||||
class Shader : public HashedObject<Shader>
|
||||
{
|
||||
public:
|
||||
Shader(Util::Hash binding, Device *device, const uint32_t *data, size_t size,
|
||||
const ResourceLayout *layout = nullptr);
|
||||
~Shader();
|
||||
|
||||
const ResourceLayout &get_layout() const
|
||||
{
|
||||
return layout;
|
||||
}
|
||||
|
||||
VkShaderModule get_module() const
|
||||
{
|
||||
return module;
|
||||
}
|
||||
|
||||
static bool reflect_resource_layout(ResourceLayout &layout, const uint32_t *spirv_data, size_t spirv_size);
|
||||
static const char *stage_to_name(ShaderStage stage);
|
||||
static Util::Hash hash(const uint32_t *data, size_t size);
|
||||
|
||||
private:
|
||||
Device *device;
|
||||
VkShaderModule module = VK_NULL_HANDLE;
|
||||
ResourceLayout layout;
|
||||
};
|
||||
|
||||
struct Pipeline
|
||||
{
|
||||
VkPipeline pipeline;
|
||||
uint32_t dynamic_mask;
|
||||
};
|
||||
|
||||
class Program : public HashedObject<Program>
|
||||
{
|
||||
public:
|
||||
Program(Device *device, Shader *vertex, Shader *fragment, const ImmutableSamplerBank *sampler_bank);
|
||||
Program(Device *device, Shader *task, Shader *mesh, Shader *fragment, const ImmutableSamplerBank *sampler_bank);
|
||||
Program(Device *device, Shader *compute, const ImmutableSamplerBank *sampler_bank);
|
||||
~Program();
|
||||
|
||||
inline const Shader *get_shader(ShaderStage stage) const
|
||||
{
|
||||
return shaders[Util::ecast(stage)];
|
||||
}
|
||||
|
||||
void set_pipeline_layout(const PipelineLayout *new_layout)
|
||||
{
|
||||
layout = new_layout;
|
||||
}
|
||||
|
||||
const PipelineLayout *get_pipeline_layout() const
|
||||
{
|
||||
return layout;
|
||||
}
|
||||
|
||||
Pipeline get_pipeline(Util::Hash hash) const;
|
||||
Pipeline add_pipeline(Util::Hash hash, const Pipeline &pipeline);
|
||||
|
||||
void promote_read_write_to_read_only();
|
||||
|
||||
private:
|
||||
void set_shader(ShaderStage stage, Shader *handle);
|
||||
Device *device;
|
||||
Shader *shaders[Util::ecast(ShaderStage::Count)] = {};
|
||||
const PipelineLayout *layout = nullptr;
|
||||
VulkanCache<Util::IntrusivePODWrapper<Pipeline>> pipelines;
|
||||
void destroy_pipeline(const Pipeline &pipeline);
|
||||
};
|
||||
}
|
||||
518
external/parallel-rdp/parallel-rdp-standalone/vulkan/texture/texture_format.cpp
vendored
Normal file
518
external/parallel-rdp/parallel-rdp-standalone/vulkan/texture/texture_format.cpp
vendored
Normal file
@@ -0,0 +1,518 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include "texture_format.hpp"
|
||||
#include "format.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
uint32_t TextureFormatLayout::num_miplevels(uint32_t width, uint32_t height, uint32_t depth)
|
||||
{
|
||||
uint32_t size = unsigned(std::max(std::max(width, height), depth));
|
||||
uint32_t levels = 0;
|
||||
while (size)
|
||||
{
|
||||
levels++;
|
||||
size >>= 1;
|
||||
}
|
||||
return levels;
|
||||
}
|
||||
|
||||
void TextureFormatLayout::format_block_dim(VkFormat format, uint32_t &width, uint32_t &height)
|
||||
{
|
||||
#define fmt(x, w, h) \
|
||||
case VK_FORMAT_##x: \
|
||||
width = w; \
|
||||
height = h; \
|
||||
break
|
||||
|
||||
switch (format)
|
||||
{
|
||||
fmt(ETC2_R8G8B8A8_UNORM_BLOCK, 4, 4);
|
||||
fmt(ETC2_R8G8B8A8_SRGB_BLOCK, 4, 4);
|
||||
fmt(ETC2_R8G8B8A1_UNORM_BLOCK, 4, 4);
|
||||
fmt(ETC2_R8G8B8A1_SRGB_BLOCK, 4, 4);
|
||||
fmt(ETC2_R8G8B8_UNORM_BLOCK, 4, 4);
|
||||
fmt(ETC2_R8G8B8_SRGB_BLOCK, 4, 4);
|
||||
fmt(EAC_R11_UNORM_BLOCK, 4, 4);
|
||||
fmt(EAC_R11_SNORM_BLOCK, 4, 4);
|
||||
fmt(EAC_R11G11_UNORM_BLOCK, 4, 4);
|
||||
fmt(EAC_R11G11_SNORM_BLOCK, 4, 4);
|
||||
|
||||
fmt(BC1_RGB_UNORM_BLOCK, 4, 4);
|
||||
fmt(BC1_RGB_SRGB_BLOCK, 4, 4);
|
||||
fmt(BC1_RGBA_UNORM_BLOCK, 4, 4);
|
||||
fmt(BC1_RGBA_SRGB_BLOCK, 4, 4);
|
||||
fmt(BC2_UNORM_BLOCK, 4, 4);
|
||||
fmt(BC2_SRGB_BLOCK, 4, 4);
|
||||
fmt(BC3_UNORM_BLOCK, 4, 4);
|
||||
fmt(BC3_SRGB_BLOCK, 4, 4);
|
||||
fmt(BC4_UNORM_BLOCK, 4, 4);
|
||||
fmt(BC4_SNORM_BLOCK, 4, 4);
|
||||
fmt(BC5_UNORM_BLOCK, 4, 4);
|
||||
fmt(BC5_SNORM_BLOCK, 4, 4);
|
||||
fmt(BC6H_UFLOAT_BLOCK, 4, 4);
|
||||
fmt(BC6H_SFLOAT_BLOCK, 4, 4);
|
||||
fmt(BC7_SRGB_BLOCK, 4, 4);
|
||||
fmt(BC7_UNORM_BLOCK, 4, 4);
|
||||
|
||||
#define astc_fmt(w, h) \
|
||||
fmt(ASTC_##w##x##h##_UNORM_BLOCK, w, h); \
|
||||
fmt(ASTC_##w##x##h##_SRGB_BLOCK, w, h); \
|
||||
fmt(ASTC_##w##x##h##_SFLOAT_BLOCK_EXT, w, h)
|
||||
|
||||
astc_fmt(4, 4);
|
||||
astc_fmt(5, 4);
|
||||
astc_fmt(5, 5);
|
||||
astc_fmt(6, 5);
|
||||
astc_fmt(6, 6);
|
||||
astc_fmt(8, 5);
|
||||
astc_fmt(8, 6);
|
||||
astc_fmt(8, 8);
|
||||
astc_fmt(10, 5);
|
||||
astc_fmt(10, 6);
|
||||
astc_fmt(10, 8);
|
||||
astc_fmt(10, 10);
|
||||
astc_fmt(12, 10);
|
||||
astc_fmt(12, 12);
|
||||
|
||||
default:
|
||||
width = 1;
|
||||
height = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
#undef fmt
|
||||
#undef astc_fmt
|
||||
}
|
||||
|
||||
uint32_t TextureFormatLayout::format_block_size(VkFormat format, VkImageAspectFlags aspect)
|
||||
{
|
||||
#define fmt(x, bpp) \
|
||||
case VK_FORMAT_##x: \
|
||||
return bpp
|
||||
|
||||
#define fmt2(x, bpp0, bpp1) \
|
||||
case VK_FORMAT_##x: \
|
||||
return aspect == VK_IMAGE_ASPECT_PLANE_0_BIT ? bpp0 : bpp1
|
||||
|
||||
switch (format)
|
||||
{
|
||||
fmt(R4G4_UNORM_PACK8, 1);
|
||||
fmt(R4G4B4A4_UNORM_PACK16, 2);
|
||||
fmt(B4G4R4A4_UNORM_PACK16, 2);
|
||||
fmt(R5G6B5_UNORM_PACK16, 2);
|
||||
fmt(B5G6R5_UNORM_PACK16, 2);
|
||||
fmt(R5G5B5A1_UNORM_PACK16, 2);
|
||||
fmt(B5G5R5A1_UNORM_PACK16, 2);
|
||||
fmt(A1R5G5B5_UNORM_PACK16, 2);
|
||||
fmt(R8_UNORM, 1);
|
||||
fmt(R8_SNORM, 1);
|
||||
fmt(R8_USCALED, 1);
|
||||
fmt(R8_SSCALED, 1);
|
||||
fmt(R8_UINT, 1);
|
||||
fmt(R8_SINT, 1);
|
||||
fmt(R8_SRGB, 1);
|
||||
fmt(R8G8_UNORM, 2);
|
||||
fmt(R8G8_SNORM, 2);
|
||||
fmt(R8G8_USCALED, 2);
|
||||
fmt(R8G8_SSCALED, 2);
|
||||
fmt(R8G8_UINT, 2);
|
||||
fmt(R8G8_SINT, 2);
|
||||
fmt(R8G8_SRGB, 2);
|
||||
fmt(R8G8B8_UNORM, 3);
|
||||
fmt(R8G8B8_SNORM, 3);
|
||||
fmt(R8G8B8_USCALED, 3);
|
||||
fmt(R8G8B8_SSCALED, 3);
|
||||
fmt(R8G8B8_UINT, 3);
|
||||
fmt(R8G8B8_SINT, 3);
|
||||
fmt(R8G8B8_SRGB, 3);
|
||||
fmt(R8G8B8A8_UNORM, 4);
|
||||
fmt(R8G8B8A8_SNORM, 4);
|
||||
fmt(R8G8B8A8_USCALED, 4);
|
||||
fmt(R8G8B8A8_SSCALED, 4);
|
||||
fmt(R8G8B8A8_UINT, 4);
|
||||
fmt(R8G8B8A8_SINT, 4);
|
||||
fmt(R8G8B8A8_SRGB, 4);
|
||||
fmt(B8G8R8A8_UNORM, 4);
|
||||
fmt(B8G8R8A8_SNORM, 4);
|
||||
fmt(B8G8R8A8_USCALED, 4);
|
||||
fmt(B8G8R8A8_SSCALED, 4);
|
||||
fmt(B8G8R8A8_UINT, 4);
|
||||
fmt(B8G8R8A8_SINT, 4);
|
||||
fmt(B8G8R8A8_SRGB, 4);
|
||||
fmt(A8B8G8R8_UNORM_PACK32, 4);
|
||||
fmt(A8B8G8R8_SNORM_PACK32, 4);
|
||||
fmt(A8B8G8R8_USCALED_PACK32, 4);
|
||||
fmt(A8B8G8R8_SSCALED_PACK32, 4);
|
||||
fmt(A8B8G8R8_UINT_PACK32, 4);
|
||||
fmt(A8B8G8R8_SINT_PACK32, 4);
|
||||
fmt(A8B8G8R8_SRGB_PACK32, 4);
|
||||
fmt(A2B10G10R10_UNORM_PACK32, 4);
|
||||
fmt(A2B10G10R10_SNORM_PACK32, 4);
|
||||
fmt(A2B10G10R10_USCALED_PACK32, 4);
|
||||
fmt(A2B10G10R10_SSCALED_PACK32, 4);
|
||||
fmt(A2B10G10R10_UINT_PACK32, 4);
|
||||
fmt(A2B10G10R10_SINT_PACK32, 4);
|
||||
fmt(A2R10G10B10_UNORM_PACK32, 4);
|
||||
fmt(A2R10G10B10_SNORM_PACK32, 4);
|
||||
fmt(A2R10G10B10_USCALED_PACK32, 4);
|
||||
fmt(A2R10G10B10_SSCALED_PACK32, 4);
|
||||
fmt(A2R10G10B10_UINT_PACK32, 4);
|
||||
fmt(A2R10G10B10_SINT_PACK32, 4);
|
||||
fmt(R16_UNORM, 2);
|
||||
fmt(R16_SNORM, 2);
|
||||
fmt(R16_USCALED, 2);
|
||||
fmt(R16_SSCALED, 2);
|
||||
fmt(R16_UINT, 2);
|
||||
fmt(R16_SINT, 2);
|
||||
fmt(R16_SFLOAT, 2);
|
||||
fmt(R16G16_UNORM, 4);
|
||||
fmt(R16G16_SNORM, 4);
|
||||
fmt(R16G16_USCALED, 4);
|
||||
fmt(R16G16_SSCALED, 4);
|
||||
fmt(R16G16_UINT, 4);
|
||||
fmt(R16G16_SINT, 4);
|
||||
fmt(R16G16_SFLOAT, 4);
|
||||
fmt(R16G16B16_UNORM, 6);
|
||||
fmt(R16G16B16_SNORM, 6);
|
||||
fmt(R16G16B16_USCALED, 6);
|
||||
fmt(R16G16B16_SSCALED, 6);
|
||||
fmt(R16G16B16_UINT, 6);
|
||||
fmt(R16G16B16_SINT, 6);
|
||||
fmt(R16G16B16_SFLOAT, 6);
|
||||
fmt(R16G16B16A16_UNORM, 8);
|
||||
fmt(R16G16B16A16_SNORM, 8);
|
||||
fmt(R16G16B16A16_USCALED, 8);
|
||||
fmt(R16G16B16A16_SSCALED, 8);
|
||||
fmt(R16G16B16A16_UINT, 8);
|
||||
fmt(R16G16B16A16_SINT, 8);
|
||||
fmt(R16G16B16A16_SFLOAT, 8);
|
||||
fmt(R32_UINT, 4);
|
||||
fmt(R32_SINT, 4);
|
||||
fmt(R32_SFLOAT, 4);
|
||||
fmt(R32G32_UINT, 8);
|
||||
fmt(R32G32_SINT, 8);
|
||||
fmt(R32G32_SFLOAT, 8);
|
||||
fmt(R32G32B32_UINT, 12);
|
||||
fmt(R32G32B32_SINT, 12);
|
||||
fmt(R32G32B32_SFLOAT, 12);
|
||||
fmt(R32G32B32A32_UINT, 16);
|
||||
fmt(R32G32B32A32_SINT, 16);
|
||||
fmt(R32G32B32A32_SFLOAT, 16);
|
||||
fmt(R64_UINT, 8);
|
||||
fmt(R64_SINT, 8);
|
||||
fmt(R64_SFLOAT, 8);
|
||||
fmt(R64G64_UINT, 16);
|
||||
fmt(R64G64_SINT, 16);
|
||||
fmt(R64G64_SFLOAT, 16);
|
||||
fmt(R64G64B64_UINT, 24);
|
||||
fmt(R64G64B64_SINT, 24);
|
||||
fmt(R64G64B64_SFLOAT, 24);
|
||||
fmt(R64G64B64A64_UINT, 32);
|
||||
fmt(R64G64B64A64_SINT, 32);
|
||||
fmt(R64G64B64A64_SFLOAT, 32);
|
||||
fmt(B10G11R11_UFLOAT_PACK32, 4);
|
||||
fmt(E5B9G9R9_UFLOAT_PACK32, 4);
|
||||
|
||||
fmt(D16_UNORM, 2);
|
||||
fmt(X8_D24_UNORM_PACK32, 4);
|
||||
fmt(D32_SFLOAT, 4);
|
||||
fmt(S8_UINT, 1);
|
||||
|
||||
case VK_FORMAT_D16_UNORM_S8_UINT:
|
||||
return aspect == VK_IMAGE_ASPECT_DEPTH_BIT ? 2 : 1;
|
||||
case VK_FORMAT_D24_UNORM_S8_UINT:
|
||||
case VK_FORMAT_D32_SFLOAT_S8_UINT:
|
||||
return aspect == VK_IMAGE_ASPECT_DEPTH_BIT ? 4 : 1;
|
||||
|
||||
// ETC2
|
||||
fmt(ETC2_R8G8B8A8_UNORM_BLOCK, 16);
|
||||
fmt(ETC2_R8G8B8A8_SRGB_BLOCK, 16);
|
||||
fmt(ETC2_R8G8B8A1_UNORM_BLOCK, 8);
|
||||
fmt(ETC2_R8G8B8A1_SRGB_BLOCK, 8);
|
||||
fmt(ETC2_R8G8B8_UNORM_BLOCK, 8);
|
||||
fmt(ETC2_R8G8B8_SRGB_BLOCK, 8);
|
||||
fmt(EAC_R11_UNORM_BLOCK, 8);
|
||||
fmt(EAC_R11_SNORM_BLOCK, 8);
|
||||
fmt(EAC_R11G11_UNORM_BLOCK, 16);
|
||||
fmt(EAC_R11G11_SNORM_BLOCK, 16);
|
||||
|
||||
// BC
|
||||
fmt(BC1_RGB_UNORM_BLOCK, 8);
|
||||
fmt(BC1_RGB_SRGB_BLOCK, 8);
|
||||
fmt(BC1_RGBA_UNORM_BLOCK, 8);
|
||||
fmt(BC1_RGBA_SRGB_BLOCK, 8);
|
||||
fmt(BC2_UNORM_BLOCK, 16);
|
||||
fmt(BC2_SRGB_BLOCK, 16);
|
||||
fmt(BC3_UNORM_BLOCK, 16);
|
||||
fmt(BC3_SRGB_BLOCK, 16);
|
||||
fmt(BC4_UNORM_BLOCK, 8);
|
||||
fmt(BC4_SNORM_BLOCK, 8);
|
||||
fmt(BC5_UNORM_BLOCK, 16);
|
||||
fmt(BC5_SNORM_BLOCK, 16);
|
||||
fmt(BC6H_UFLOAT_BLOCK, 16);
|
||||
fmt(BC6H_SFLOAT_BLOCK, 16);
|
||||
fmt(BC7_SRGB_BLOCK, 16);
|
||||
fmt(BC7_UNORM_BLOCK, 16);
|
||||
|
||||
// ASTC
|
||||
#define astc_fmt(w, h) \
|
||||
fmt(ASTC_##w##x##h##_UNORM_BLOCK, 16); \
|
||||
fmt(ASTC_##w##x##h##_SRGB_BLOCK, 16); \
|
||||
fmt(ASTC_##w##x##h##_SFLOAT_BLOCK_EXT, 16)
|
||||
|
||||
astc_fmt(4, 4);
|
||||
astc_fmt(5, 4);
|
||||
astc_fmt(5, 5);
|
||||
astc_fmt(6, 5);
|
||||
astc_fmt(6, 6);
|
||||
astc_fmt(8, 5);
|
||||
astc_fmt(8, 6);
|
||||
astc_fmt(8, 8);
|
||||
astc_fmt(10, 5);
|
||||
astc_fmt(10, 6);
|
||||
astc_fmt(10, 8);
|
||||
astc_fmt(10, 10);
|
||||
astc_fmt(12, 10);
|
||||
astc_fmt(12, 12);
|
||||
|
||||
fmt(G8B8G8R8_422_UNORM, 4);
|
||||
fmt(B8G8R8G8_422_UNORM, 4);
|
||||
|
||||
fmt(G8_B8_R8_3PLANE_420_UNORM, 1);
|
||||
fmt2(G8_B8R8_2PLANE_420_UNORM, 1, 2);
|
||||
fmt(G8_B8_R8_3PLANE_422_UNORM, 1);
|
||||
fmt2(G8_B8R8_2PLANE_422_UNORM, 1, 2);
|
||||
fmt(G8_B8_R8_3PLANE_444_UNORM, 1);
|
||||
|
||||
fmt(R10X6_UNORM_PACK16, 2);
|
||||
fmt(R10X6G10X6_UNORM_2PACK16, 4);
|
||||
fmt(R10X6G10X6B10X6A10X6_UNORM_4PACK16, 8);
|
||||
fmt(G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, 8);
|
||||
fmt(B10X6G10X6R10X6G10X6_422_UNORM_4PACK16, 8);
|
||||
fmt(G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16, 2);
|
||||
fmt(G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16, 2);
|
||||
fmt(G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, 2);
|
||||
fmt2(G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, 2, 4);
|
||||
fmt2(G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, 2, 4);
|
||||
|
||||
fmt(R12X4_UNORM_PACK16, 2);
|
||||
fmt(R12X4G12X4_UNORM_2PACK16, 4);
|
||||
fmt(R12X4G12X4B12X4A12X4_UNORM_4PACK16, 8);
|
||||
fmt(G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, 8);
|
||||
fmt(B12X4G12X4R12X4G12X4_422_UNORM_4PACK16, 8);
|
||||
fmt(G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16, 2);
|
||||
fmt(G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16, 2);
|
||||
fmt(G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16, 2);
|
||||
fmt2(G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, 2, 4);
|
||||
fmt2(G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16, 2, 4);
|
||||
|
||||
fmt(G16B16G16R16_422_UNORM, 8);
|
||||
fmt(B16G16R16G16_422_UNORM, 8);
|
||||
fmt(G16_B16_R16_3PLANE_420_UNORM, 2);
|
||||
fmt(G16_B16_R16_3PLANE_422_UNORM, 2);
|
||||
fmt(G16_B16_R16_3PLANE_444_UNORM, 2);
|
||||
fmt2(G16_B16R16_2PLANE_420_UNORM, 2, 4);
|
||||
fmt2(G16_B16R16_2PLANE_422_UNORM, 2, 4);
|
||||
|
||||
default:
|
||||
assert(0 && "Unknown format.");
|
||||
return 0;
|
||||
}
|
||||
#undef fmt
|
||||
#undef fmt2
|
||||
#undef astc_fmt
|
||||
}
|
||||
|
||||
void TextureFormatLayout::fill_mipinfo(uint32_t width, uint32_t height, uint32_t depth)
|
||||
{
|
||||
block_stride = format_block_size(format, 0);
|
||||
format_block_dim(format, block_dim_x, block_dim_y);
|
||||
|
||||
if (mip_levels == 0)
|
||||
mip_levels = num_miplevels(width, height, depth);
|
||||
|
||||
size_t offset = 0;
|
||||
|
||||
for (uint32_t mip = 0; mip < mip_levels; mip++)
|
||||
{
|
||||
offset = (offset + 15) & ~15;
|
||||
|
||||
uint32_t blocks_x = (width + block_dim_x - 1) / block_dim_x;
|
||||
uint32_t blocks_y = (height + block_dim_y - 1) / block_dim_y;
|
||||
size_t mip_size = blocks_x * blocks_y * array_layers * depth * block_stride;
|
||||
|
||||
mips[mip].offset = offset;
|
||||
|
||||
mips[mip].block_row_length = blocks_x;
|
||||
mips[mip].block_image_height = blocks_y;
|
||||
|
||||
mips[mip].row_length = blocks_x * block_dim_x;
|
||||
mips[mip].image_height = blocks_y * block_dim_y;
|
||||
|
||||
mips[mip].width = width;
|
||||
mips[mip].height = height;
|
||||
mips[mip].depth = depth;
|
||||
|
||||
offset += mip_size;
|
||||
|
||||
width = std::max((width >> 1u), 1u);
|
||||
height = std::max((height >> 1u), 1u);
|
||||
depth = std::max((depth >> 1u), 1u);
|
||||
}
|
||||
|
||||
required_size = offset;
|
||||
}
|
||||
|
||||
void TextureFormatLayout::set_1d(VkFormat format_, uint32_t width, uint32_t array_layers_, uint32_t mip_levels_)
|
||||
{
|
||||
image_type = VK_IMAGE_TYPE_1D;
|
||||
format = format_;
|
||||
array_layers = array_layers_;
|
||||
mip_levels = mip_levels_;
|
||||
|
||||
fill_mipinfo(width, 1, 1);
|
||||
}
|
||||
|
||||
void TextureFormatLayout::set_2d(VkFormat format_, uint32_t width, uint32_t height,
|
||||
uint32_t array_layers_, uint32_t mip_levels_)
|
||||
{
|
||||
image_type = VK_IMAGE_TYPE_2D;
|
||||
format = format_;
|
||||
array_layers = array_layers_;
|
||||
mip_levels = mip_levels_;
|
||||
|
||||
fill_mipinfo(width, height, 1);
|
||||
}
|
||||
|
||||
void TextureFormatLayout::set_3d(VkFormat format_, uint32_t width, uint32_t height, uint32_t depth, uint32_t mip_levels_)
|
||||
{
|
||||
image_type = VK_IMAGE_TYPE_3D;
|
||||
format = format_;
|
||||
array_layers = 1;
|
||||
mip_levels = mip_levels_;
|
||||
|
||||
fill_mipinfo(width, height, depth);
|
||||
}
|
||||
|
||||
void TextureFormatLayout::set_buffer(void *buffer_, size_t size)
|
||||
{
|
||||
buffer = static_cast<uint8_t *>(buffer_);
|
||||
buffer_size = size;
|
||||
}
|
||||
|
||||
uint32_t TextureFormatLayout::get_width(uint32_t mip) const
|
||||
{
|
||||
return mips[mip].width;
|
||||
}
|
||||
|
||||
uint32_t TextureFormatLayout::get_height(uint32_t mip) const
|
||||
{
|
||||
return mips[mip].height;
|
||||
}
|
||||
|
||||
uint32_t TextureFormatLayout::get_depth(uint32_t mip) const
|
||||
{
|
||||
return mips[mip].depth;
|
||||
}
|
||||
|
||||
uint32_t TextureFormatLayout::get_layers() const
|
||||
{
|
||||
return array_layers;
|
||||
}
|
||||
|
||||
VkImageType TextureFormatLayout::get_image_type() const
|
||||
{
|
||||
return image_type;
|
||||
}
|
||||
|
||||
VkFormat TextureFormatLayout::get_format() const
|
||||
{
|
||||
return format;
|
||||
}
|
||||
|
||||
uint32_t TextureFormatLayout::get_block_stride() const
|
||||
{
|
||||
return block_stride;
|
||||
}
|
||||
|
||||
uint32_t TextureFormatLayout::get_levels() const
|
||||
{
|
||||
return mip_levels;
|
||||
}
|
||||
|
||||
size_t TextureFormatLayout::get_required_size() const
|
||||
{
|
||||
return required_size;
|
||||
}
|
||||
|
||||
const TextureFormatLayout::MipInfo &TextureFormatLayout::get_mip_info(uint32_t mip) const
|
||||
{
|
||||
return mips[mip];
|
||||
}
|
||||
|
||||
uint32_t TextureFormatLayout::get_block_dim_x() const
|
||||
{
|
||||
return block_dim_x;
|
||||
}
|
||||
|
||||
uint32_t TextureFormatLayout::get_block_dim_y() const
|
||||
{
|
||||
return block_dim_y;
|
||||
}
|
||||
|
||||
size_t TextureFormatLayout::row_byte_stride(uint32_t row_length) const
|
||||
{
|
||||
return ((row_length + block_dim_x - 1) / block_dim_x) * block_stride;
|
||||
}
|
||||
|
||||
size_t TextureFormatLayout::layer_byte_stride(uint32_t image_height, size_t row_byte_stride) const
|
||||
{
|
||||
return ((image_height + block_dim_y - 1) / block_dim_y) * row_byte_stride;
|
||||
}
|
||||
|
||||
void TextureFormatLayout::build_buffer_image_copies(Util::SmallVector<VkBufferImageCopy, 32> &copies) const
|
||||
{
|
||||
copies.resize(mip_levels);
|
||||
for (unsigned level = 0; level < mip_levels; level++)
|
||||
{
|
||||
const auto &mip_info = mips[level];
|
||||
|
||||
auto &blit = copies[level];
|
||||
blit = {};
|
||||
blit.bufferOffset = mip_info.offset;
|
||||
blit.bufferRowLength = mip_info.row_length;
|
||||
blit.bufferImageHeight = mip_info.image_height;
|
||||
blit.imageSubresource.aspectMask = format_to_aspect_mask(format);
|
||||
blit.imageSubresource.mipLevel = level;
|
||||
blit.imageSubresource.baseArrayLayer = 0;
|
||||
blit.imageSubresource.layerCount = array_layers;
|
||||
blit.imageExtent.width = mip_info.width;
|
||||
blit.imageExtent.height = mip_info.height;
|
||||
blit.imageExtent.depth = mip_info.depth;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
178
external/parallel-rdp/parallel-rdp-standalone/vulkan/texture/texture_format.hpp
vendored
Normal file
178
external/parallel-rdp/parallel-rdp-standalone/vulkan/texture/texture_format.hpp
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vulkan_headers.hpp"
|
||||
#include "small_vector.hpp"
|
||||
#include <vector>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class TextureFormatLayout
|
||||
{
|
||||
public:
|
||||
void set_1d(VkFormat format, uint32_t width, uint32_t array_layers = 1, uint32_t mip_levels = 1);
|
||||
void set_2d(VkFormat format, uint32_t width, uint32_t height, uint32_t array_layers = 1, uint32_t mip_levels = 1);
|
||||
void set_3d(VkFormat format, uint32_t width, uint32_t height, uint32_t depth, uint32_t mip_levels = 1);
|
||||
|
||||
static uint32_t format_block_size(VkFormat format, VkImageAspectFlags aspect);
|
||||
static void format_block_dim(VkFormat format, uint32_t &width, uint32_t &height);
|
||||
static uint32_t num_miplevels(uint32_t width, uint32_t height = 1, uint32_t depth = 1);
|
||||
|
||||
void set_buffer(void *buffer, size_t size);
|
||||
inline void *get_buffer()
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
uint32_t get_width(uint32_t mip = 0) const;
|
||||
uint32_t get_height(uint32_t mip = 0) const;
|
||||
uint32_t get_depth(uint32_t mip = 0) const;
|
||||
uint32_t get_levels() const;
|
||||
uint32_t get_layers() const;
|
||||
uint32_t get_block_stride() const;
|
||||
uint32_t get_block_dim_x() const;
|
||||
uint32_t get_block_dim_y() const;
|
||||
VkImageType get_image_type() const;
|
||||
VkFormat get_format() const;
|
||||
|
||||
size_t get_required_size() const;
|
||||
|
||||
size_t row_byte_stride(uint32_t row_length) const;
|
||||
size_t layer_byte_stride(uint32_t row_length, size_t row_byte_stride) const;
|
||||
|
||||
inline size_t get_row_size(uint32_t mip) const
|
||||
{
|
||||
return size_t(mips[mip].block_row_length) * block_stride;
|
||||
}
|
||||
|
||||
inline size_t get_layer_size(uint32_t mip) const
|
||||
{
|
||||
return size_t(mips[mip].block_image_height) * get_row_size(mip);
|
||||
}
|
||||
|
||||
struct MipInfo
|
||||
{
|
||||
size_t offset = 0;
|
||||
uint32_t width = 1;
|
||||
uint32_t height = 1;
|
||||
uint32_t depth = 1;
|
||||
|
||||
uint32_t block_image_height = 0;
|
||||
uint32_t block_row_length = 0;
|
||||
uint32_t image_height = 0;
|
||||
uint32_t row_length = 0;
|
||||
};
|
||||
|
||||
const MipInfo &get_mip_info(uint32_t mip) const;
|
||||
|
||||
inline void *data(uint32_t layer = 0, uint32_t mip = 0) const
|
||||
{
|
||||
assert(buffer);
|
||||
assert(buffer_size == required_size);
|
||||
auto &mip_info = mips[mip];
|
||||
uint8_t *slice = buffer + mip_info.offset;
|
||||
slice += block_stride * layer * mip_info.block_row_length * mip_info.block_image_height;
|
||||
return slice;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T *data_generic(uint32_t x, uint32_t y, uint32_t slice_index, uint32_t mip = 0) const
|
||||
{
|
||||
auto &mip_info = mips[mip];
|
||||
T *slice = reinterpret_cast<T *>(buffer + mip_info.offset);
|
||||
slice += slice_index * mip_info.block_row_length * mip_info.block_image_height;
|
||||
slice += y * mip_info.block_row_length;
|
||||
slice += x;
|
||||
return slice;
|
||||
}
|
||||
|
||||
inline void *data_opaque(uint32_t x, uint32_t y, uint32_t slice_index, uint32_t mip = 0) const
|
||||
{
|
||||
auto &mip_info = mips[mip];
|
||||
uint8_t *slice = buffer + mip_info.offset;
|
||||
size_t off = slice_index * mip_info.block_row_length * mip_info.block_image_height;
|
||||
off += y * mip_info.block_row_length;
|
||||
off += x;
|
||||
return slice + off * block_stride;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T *data_generic() const
|
||||
{
|
||||
return data_generic<T>(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T *data_1d(uint32_t x, uint32_t layer = 0, uint32_t mip = 0) const
|
||||
{
|
||||
assert(sizeof(T) == block_stride);
|
||||
assert(buffer);
|
||||
assert(image_type == VK_IMAGE_TYPE_1D);
|
||||
assert(buffer_size == required_size);
|
||||
return data_generic<T>(x, 0, layer, mip);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T *data_2d(uint32_t x, uint32_t y, uint32_t layer = 0, uint32_t mip = 0) const
|
||||
{
|
||||
assert(sizeof(T) == block_stride);
|
||||
assert(buffer);
|
||||
assert(image_type == VK_IMAGE_TYPE_2D);
|
||||
assert(buffer_size == required_size);
|
||||
return data_generic<T>(x, y, layer, mip);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T *data_3d(uint32_t x, uint32_t y, uint32_t z, uint32_t mip = 0) const
|
||||
{
|
||||
assert(sizeof(T) == block_stride);
|
||||
assert(buffer);
|
||||
assert(image_type == VK_IMAGE_TYPE_3D);
|
||||
assert(buffer_size == required_size);
|
||||
return data_generic<T>(x, y, z, mip);
|
||||
}
|
||||
|
||||
void build_buffer_image_copies(Util::SmallVector<VkBufferImageCopy, 32> &copies) const;
|
||||
|
||||
private:
|
||||
uint8_t *buffer = nullptr;
|
||||
size_t buffer_size = 0;
|
||||
|
||||
VkImageType image_type = VK_IMAGE_TYPE_MAX_ENUM;
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
size_t required_size = 0;
|
||||
|
||||
uint32_t block_stride = 1;
|
||||
uint32_t mip_levels = 1;
|
||||
uint32_t array_layers = 1;
|
||||
uint32_t block_dim_x = 1;
|
||||
uint32_t block_dim_y = 1;
|
||||
|
||||
MipInfo mips[16];
|
||||
|
||||
void fill_mipinfo(uint32_t width, uint32_t height, uint32_t depth);
|
||||
};
|
||||
}
|
||||
113
external/parallel-rdp/parallel-rdp-standalone/vulkan/type_to_string.hpp
vendored
Normal file
113
external/parallel-rdp/parallel-rdp-standalone/vulkan/type_to_string.hpp
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
static inline const char *layout_to_string(VkImageLayout layout)
|
||||
{
|
||||
switch (layout)
|
||||
{
|
||||
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
||||
return "SHADER_READ_ONLY";
|
||||
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
|
||||
return "DS_READ_ONLY";
|
||||
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
|
||||
return "DS";
|
||||
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
|
||||
return "COLOR";
|
||||
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
|
||||
return "TRANSFER_DST";
|
||||
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
|
||||
return "TRANSFER_SRC";
|
||||
case VK_IMAGE_LAYOUT_GENERAL:
|
||||
return "GENERAL";
|
||||
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
|
||||
return "PRESENT";
|
||||
default:
|
||||
return "UNDEFINED";
|
||||
}
|
||||
}
|
||||
|
||||
static inline std::string access_flags_to_string(VkAccessFlags2 flags)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
if (flags & VK_ACCESS_SHADER_READ_BIT)
|
||||
result += "SHADER_READ ";
|
||||
if (flags & VK_ACCESS_SHADER_WRITE_BIT)
|
||||
result += "SHADER_WRITE ";
|
||||
if (flags & VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT)
|
||||
result += "DS_WRITE ";
|
||||
if (flags & VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT)
|
||||
result += "DS_READ ";
|
||||
if (flags & VK_ACCESS_COLOR_ATTACHMENT_READ_BIT)
|
||||
result += "COLOR_READ ";
|
||||
if (flags & VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT)
|
||||
result += "COLOR_WRITE ";
|
||||
if (flags & VK_ACCESS_INPUT_ATTACHMENT_READ_BIT)
|
||||
result += "INPUT_READ ";
|
||||
if (flags & VK_ACCESS_TRANSFER_WRITE_BIT)
|
||||
result += "TRANSFER_WRITE ";
|
||||
if (flags & VK_ACCESS_TRANSFER_READ_BIT)
|
||||
result += "TRANSFER_READ ";
|
||||
if (flags & VK_ACCESS_UNIFORM_READ_BIT)
|
||||
result += "UNIFORM_READ ";
|
||||
|
||||
if (!result.empty())
|
||||
result.pop_back();
|
||||
else
|
||||
result = "NONE";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline std::string stage_flags_to_string(VkPipelineStageFlags2 flags)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
if (flags & VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT)
|
||||
result += "GRAPHICS ";
|
||||
if (flags & (VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT))
|
||||
result += "DEPTH ";
|
||||
if (flags & VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)
|
||||
result += "COLOR ";
|
||||
if (flags & VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT)
|
||||
result += "FRAGMENT ";
|
||||
if (flags & VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT)
|
||||
result += "COMPUTE ";
|
||||
if (flags & VK_PIPELINE_STAGE_TRANSFER_BIT)
|
||||
result += "TRANSFER ";
|
||||
if (flags & (VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT))
|
||||
result += "VERTEX ";
|
||||
|
||||
if (!result.empty())
|
||||
result.pop_back();
|
||||
else
|
||||
result = "NONE";
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
111
external/parallel-rdp/parallel-rdp-standalone/vulkan/vulkan_common.hpp
vendored
Normal file
111
external/parallel-rdp/parallel-rdp-standalone/vulkan/vulkan_common.hpp
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "intrusive.hpp"
|
||||
#include "object_pool.hpp"
|
||||
#include "intrusive_hash_map.hpp"
|
||||
#include "vulkan_headers.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
using HandleCounter = Util::MultiThreadCounter;
|
||||
|
||||
template <typename T>
|
||||
using VulkanObjectPool = Util::ThreadSafeObjectPool<T>;
|
||||
template <typename T>
|
||||
using VulkanCache = Util::ThreadSafeIntrusiveHashMapReadCached<T>;
|
||||
template <typename T>
|
||||
using VulkanCacheReadWrite = Util::ThreadSafeIntrusiveHashMap<T>;
|
||||
|
||||
enum QueueIndices
|
||||
{
|
||||
QUEUE_INDEX_GRAPHICS,
|
||||
QUEUE_INDEX_COMPUTE,
|
||||
QUEUE_INDEX_TRANSFER,
|
||||
QUEUE_INDEX_VIDEO_DECODE,
|
||||
QUEUE_INDEX_VIDEO_ENCODE,
|
||||
QUEUE_INDEX_COUNT
|
||||
};
|
||||
|
||||
struct ExternalHandle
|
||||
{
|
||||
#ifdef _WIN32
|
||||
using NativeHandle = void *;
|
||||
NativeHandle handle = nullptr;
|
||||
#else
|
||||
using NativeHandle = int;
|
||||
NativeHandle handle = -1;
|
||||
#endif
|
||||
|
||||
VkExternalMemoryHandleTypeFlagBits memory_handle_type = get_opaque_memory_handle_type();
|
||||
VkExternalSemaphoreHandleTypeFlagBits semaphore_handle_type = get_opaque_semaphore_handle_type();
|
||||
|
||||
constexpr static VkExternalMemoryHandleTypeFlagBits get_opaque_memory_handle_type()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
|
||||
#else
|
||||
return VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr static VkExternalSemaphoreHandleTypeFlagBits get_opaque_semaphore_handle_type()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT;
|
||||
#else
|
||||
return VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline explicit operator bool() const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return handle != nullptr;
|
||||
#else
|
||||
return handle >= 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool memory_handle_type_imports_by_reference(VkExternalMemoryHandleTypeFlagBits type)
|
||||
{
|
||||
VK_ASSERT(type == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT ||
|
||||
type == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT ||
|
||||
type == VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT ||
|
||||
type == VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP_BIT ||
|
||||
type == VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT);
|
||||
return type != VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
|
||||
}
|
||||
|
||||
static bool semaphore_handle_type_imports_by_reference(VkExternalSemaphoreHandleTypeFlagBits type)
|
||||
{
|
||||
VK_ASSERT(type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT ||
|
||||
type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT ||
|
||||
type == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT);
|
||||
|
||||
// D3D11 fence aliases D3D12 fence. It's basically the same thing, just D3D11.3.
|
||||
return type != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
|
||||
}
|
||||
};
|
||||
}
|
||||
71
external/parallel-rdp/parallel-rdp-standalone/vulkan/vulkan_headers.hpp
vendored
Normal file
71
external/parallel-rdp/parallel-rdp-standalone/vulkan/vulkan_headers.hpp
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(_WIN32) && !defined(VK_USE_PLATFORM_WIN32_KHR)
|
||||
#define VK_USE_PLATFORM_WIN32_KHR
|
||||
#endif
|
||||
|
||||
#if defined(VULKAN_H_) || defined(VULKAN_CORE_H_)
|
||||
#error "Must include vulkan_headers.hpp before Vulkan headers"
|
||||
#endif
|
||||
|
||||
#include "volk.h"
|
||||
#include <stdlib.h>
|
||||
#include "logging.hpp"
|
||||
#include <utility>
|
||||
|
||||
// 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) \
|
||||
do \
|
||||
{ \
|
||||
if (!bool(x)) \
|
||||
{ \
|
||||
LOGE("Vulkan error at %s:%d.\n", __FILE__, __LINE__); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define VK_ASSERT(x) ((void)0)
|
||||
#endif
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
struct NoCopyNoMove
|
||||
{
|
||||
NoCopyNoMove() = default;
|
||||
NoCopyNoMove(const NoCopyNoMove &) = delete;
|
||||
void operator=(const NoCopyNoMove &) = delete;
|
||||
};
|
||||
}
|
||||
169
external/parallel-rdp/parallel-rdp-standalone/vulkan/vulkan_prerotate.hpp
vendored
Normal file
169
external/parallel-rdp/parallel-rdp-standalone/vulkan/vulkan_prerotate.hpp
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vulkan_headers.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
// FIXME: Also consider that we might have to flip X or Y w.r.t. dimensions,
|
||||
// but that only matters for partial rendering ...
|
||||
static inline bool surface_transform_swaps_xy(VkSurfaceTransformFlagBitsKHR transform)
|
||||
{
|
||||
return (transform & (
|
||||
VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR |
|
||||
VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR |
|
||||
VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR |
|
||||
VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR)) != 0;
|
||||
}
|
||||
|
||||
static inline void viewport_transform_xy(VkViewport &vp, VkSurfaceTransformFlagBitsKHR transform,
|
||||
uint32_t fb_width, uint32_t fb_height)
|
||||
{
|
||||
switch (transform)
|
||||
{
|
||||
case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
|
||||
{
|
||||
float new_y = vp.x;
|
||||
float new_x = float(fb_width) - (vp.y + vp.height);
|
||||
vp.x = new_x;
|
||||
vp.y = new_y;
|
||||
std::swap(vp.width, vp.height);
|
||||
break;
|
||||
}
|
||||
|
||||
case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
|
||||
{
|
||||
// Untested. Cannot make Android trigger this mode.
|
||||
float new_left = float(fb_width) - (vp.x + vp.width);
|
||||
float new_top = float(fb_height) - (vp.y + vp.height);
|
||||
vp.x = new_left;
|
||||
vp.y = new_top;
|
||||
break;
|
||||
}
|
||||
|
||||
case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
|
||||
{
|
||||
float new_x = vp.y;
|
||||
float new_y = float(fb_height) - (vp.x + vp.width);
|
||||
vp.x = new_x;
|
||||
vp.y = new_y;
|
||||
std::swap(vp.width, vp.height);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void rect2d_clip(VkRect2D &rect)
|
||||
{
|
||||
if (rect.offset.x < 0)
|
||||
{
|
||||
rect.extent.width += rect.offset.x;
|
||||
rect.offset.x = 0;
|
||||
}
|
||||
|
||||
if (rect.offset.y < 0)
|
||||
{
|
||||
rect.extent.height += rect.offset.y;
|
||||
rect.offset.y = 0;
|
||||
}
|
||||
|
||||
rect.extent.width = std::min<uint32_t>(rect.extent.width, 0x7fffffffu - rect.offset.x);
|
||||
rect.extent.height = std::min<uint32_t>(rect.extent.height, 0x7fffffffu - rect.offset.y);
|
||||
}
|
||||
|
||||
static inline void rect2d_transform_xy(VkRect2D &rect, VkSurfaceTransformFlagBitsKHR transform,
|
||||
uint32_t fb_width, uint32_t fb_height)
|
||||
{
|
||||
switch (transform)
|
||||
{
|
||||
case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
|
||||
{
|
||||
int new_y = rect.offset.x;
|
||||
int new_x = int(fb_width) - int(rect.offset.y + rect.extent.height);
|
||||
rect.offset = { new_x, new_y };
|
||||
std::swap(rect.extent.width, rect.extent.height);
|
||||
break;
|
||||
}
|
||||
|
||||
case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
|
||||
{
|
||||
// Untested. Cannot make Android trigger this mode.
|
||||
int new_left = int(fb_width) - int(rect.offset.x + rect.extent.width);
|
||||
int new_top = int(fb_height) - int(rect.offset.y + rect.extent.height);
|
||||
rect.offset = { new_left, new_top };
|
||||
break;
|
||||
}
|
||||
|
||||
case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
|
||||
{
|
||||
int new_x = rect.offset.y;
|
||||
int new_y = int(fb_height) - int(rect.offset.x + rect.extent.width);
|
||||
rect.offset = { new_x, new_y };
|
||||
std::swap(rect.extent.width, rect.extent.height);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void build_prerotate_matrix_2x2(VkSurfaceTransformFlagBitsKHR pre_rotate, float mat[4])
|
||||
{
|
||||
// TODO: HORIZONTAL_MIRROR.
|
||||
switch (pre_rotate)
|
||||
{
|
||||
default:
|
||||
mat[0] = 1.0f;
|
||||
mat[1] = 0.0f;
|
||||
mat[2] = 0.0f;
|
||||
mat[3] = 1.0f;
|
||||
break;
|
||||
|
||||
case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
|
||||
mat[0] = 0.0f;
|
||||
mat[1] = 1.0f;
|
||||
mat[2] = -1.0f;
|
||||
mat[3] = 0.0f;
|
||||
break;
|
||||
|
||||
case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
|
||||
mat[0] = 0.0f;
|
||||
mat[1] = -1.0f;
|
||||
mat[2] = 1.0f;
|
||||
mat[3] = 0.0f;
|
||||
break;
|
||||
|
||||
case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
|
||||
mat[0] = -1.0f;
|
||||
mat[1] = 0.0f;
|
||||
mat[2] = 0.0f;
|
||||
mat[3] = -1.0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
1769
external/parallel-rdp/parallel-rdp-standalone/vulkan/wsi.cpp
vendored
Normal file
1769
external/parallel-rdp/parallel-rdp-standalone/vulkan/wsi.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
379
external/parallel-rdp/parallel-rdp-standalone/vulkan/wsi.hpp
vendored
Normal file
379
external/parallel-rdp/parallel-rdp-standalone/vulkan/wsi.hpp
vendored
Normal file
@@ -0,0 +1,379 @@
|
||||
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "device.hpp"
|
||||
#include "semaphore_manager.hpp"
|
||||
#include "vulkan_headers.hpp"
|
||||
#include "timer.hpp"
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
#ifdef HAVE_WSI_DXGI_INTEROP
|
||||
#include "wsi_dxgi.hpp"
|
||||
#endif
|
||||
|
||||
namespace Granite
|
||||
{
|
||||
class InputTrackerHandler;
|
||||
}
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class WSI;
|
||||
|
||||
class WSIPlatform
|
||||
{
|
||||
public:
|
||||
virtual ~WSIPlatform() = default;
|
||||
|
||||
virtual VkSurfaceKHR create_surface(VkInstance instance, VkPhysicalDevice gpu) = 0;
|
||||
// This is virtual so that application can hold ownership over the surface handle, for e.g. Qt interop.
|
||||
virtual void destroy_surface(VkInstance instance, VkSurfaceKHR surface);
|
||||
virtual std::vector<const char *> get_instance_extensions() = 0;
|
||||
virtual std::vector<const char *> get_device_extensions()
|
||||
{
|
||||
return { "VK_KHR_swapchain" };
|
||||
}
|
||||
|
||||
virtual VkFormat get_preferred_format()
|
||||
{
|
||||
return VK_FORMAT_B8G8R8A8_SRGB;
|
||||
}
|
||||
|
||||
bool should_resize()
|
||||
{
|
||||
return resize;
|
||||
}
|
||||
|
||||
virtual void notify_current_swapchain_dimensions(unsigned width, unsigned height)
|
||||
{
|
||||
resize = false;
|
||||
current_swapchain_width = width;
|
||||
current_swapchain_height = height;
|
||||
}
|
||||
|
||||
virtual uint32_t get_surface_width() = 0;
|
||||
virtual uint32_t get_surface_height() = 0;
|
||||
|
||||
virtual float get_aspect_ratio()
|
||||
{
|
||||
return float(get_surface_width()) / float(get_surface_height());
|
||||
}
|
||||
|
||||
virtual bool alive(WSI &wsi) = 0;
|
||||
virtual void poll_input() = 0;
|
||||
virtual void poll_input_async(Granite::InputTrackerHandler *handler) = 0;
|
||||
virtual bool has_external_swapchain()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void block_until_wsi_forward_progress(WSI &wsi)
|
||||
{
|
||||
get_frame_timer().enter_idle();
|
||||
while (!resize && alive(wsi))
|
||||
{
|
||||
poll_input();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
get_frame_timer().leave_idle();
|
||||
}
|
||||
|
||||
Util::FrameTimer &get_frame_timer()
|
||||
{
|
||||
return timer;
|
||||
}
|
||||
|
||||
virtual void release_resources()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void event_device_created(Device *device);
|
||||
virtual void event_device_destroyed();
|
||||
virtual void event_swapchain_created(Device *device, VkSwapchainKHR swapchain,
|
||||
unsigned width, unsigned height,
|
||||
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);
|
||||
|
||||
virtual void set_window_title(const std::string &title);
|
||||
|
||||
virtual uintptr_t get_fullscreen_monitor();
|
||||
virtual uintptr_t get_native_window();
|
||||
|
||||
virtual const VkApplicationInfo *get_application_info();
|
||||
|
||||
virtual void begin_drop_event();
|
||||
|
||||
enum class MessageType { Error, Warning, Info };
|
||||
virtual void show_message_box(const std::string &str, MessageType type);
|
||||
|
||||
protected:
|
||||
unsigned current_swapchain_width = 0;
|
||||
unsigned current_swapchain_height = 0;
|
||||
bool resize = false;
|
||||
|
||||
private:
|
||||
Util::FrameTimer timer;
|
||||
};
|
||||
|
||||
enum class PresentMode
|
||||
{
|
||||
SyncToVBlank, // Force FIFO
|
||||
UnlockedMaybeTear, // MAILBOX or IMMEDIATE
|
||||
UnlockedForceTearing, // Force IMMEDIATE
|
||||
UnlockedNoTearing // Force MAILBOX
|
||||
};
|
||||
|
||||
enum class BackbufferFormat
|
||||
{
|
||||
UNORM,
|
||||
sRGB,
|
||||
HDR10,
|
||||
DisplayP3,
|
||||
UNORMPassthrough
|
||||
};
|
||||
|
||||
class WSI
|
||||
{
|
||||
public:
|
||||
WSI();
|
||||
void set_platform(WSIPlatform *platform);
|
||||
void set_present_mode(PresentMode mode);
|
||||
void set_backbuffer_format(BackbufferFormat format);
|
||||
|
||||
// Latency is normally pretty low, but this aims to target
|
||||
// really low latency. Only suitable for cases where rendering loads are extremely simple.
|
||||
void set_low_latency_mode(bool enable);
|
||||
|
||||
inline BackbufferFormat get_backbuffer_format() const
|
||||
{
|
||||
return backbuffer_format;
|
||||
}
|
||||
|
||||
inline VkColorSpaceKHR get_backbuffer_color_space() const
|
||||
{
|
||||
return swapchain_surface_format.colorSpace;
|
||||
}
|
||||
|
||||
void set_support_prerotate(bool enable);
|
||||
void set_extra_usage_flags(VkImageUsageFlags usage);
|
||||
VkSurfaceTransformFlagBitsKHR get_current_prerotate() const;
|
||||
|
||||
inline PresentMode get_present_mode() const
|
||||
{
|
||||
return present_mode;
|
||||
}
|
||||
|
||||
// Deprecated, use set_backbuffer_format().
|
||||
void set_backbuffer_srgb(bool enable);
|
||||
inline bool get_backbuffer_srgb() const
|
||||
{
|
||||
return backbuffer_format == BackbufferFormat::sRGB;
|
||||
}
|
||||
|
||||
void set_hdr_metadata(const VkHdrMetadataEXT &metadata);
|
||||
inline const VkHdrMetadataEXT &get_hdr_metadata() const
|
||||
{
|
||||
return hdr_metadata;
|
||||
}
|
||||
|
||||
// First, we need a Util::IntrinsivePtr<Vulkan::Context>.
|
||||
// This holds the instance and device.
|
||||
|
||||
// The simple approach. WSI internally creates the context with instance + device.
|
||||
// Required information about extensions etc, is pulled from the platform.
|
||||
bool init_context_from_platform(unsigned num_thread_indices, const Context::SystemHandles &system_handles);
|
||||
|
||||
// If you have your own VkInstance and/or VkDevice, you must create your own Vulkan::Context with
|
||||
// the appropriate init() call. Based on the platform you use, you must make sure to enable the
|
||||
// required extensions.
|
||||
bool init_from_existing_context(ContextHandle context);
|
||||
|
||||
// Then we initialize the Vulkan::Device. Either lets WSI create its own device or reuse an existing handle.
|
||||
// A device provided here must have been bound to the context.
|
||||
bool init_device();
|
||||
bool init_device(DeviceHandle device);
|
||||
|
||||
// Called after we have a device and context.
|
||||
// Either we can use a swapchain based on VkSurfaceKHR, or we can supply our own images
|
||||
// to create a virtual swapchain.
|
||||
// init_surface_swapchain() is called once.
|
||||
// Here we create the surface and perform creation of the first swapchain.
|
||||
bool init_surface_swapchain();
|
||||
bool init_external_swapchain(std::vector<ImageHandle> external_images);
|
||||
|
||||
// Calls init_context_from_platform -> init_device -> init_surface_swapchain in succession.
|
||||
bool init_simple(unsigned num_thread_indices, const Context::SystemHandles &system_handles);
|
||||
|
||||
~WSI();
|
||||
|
||||
inline Context &get_context()
|
||||
{
|
||||
return *context;
|
||||
}
|
||||
|
||||
inline Device &get_device()
|
||||
{
|
||||
return *device;
|
||||
}
|
||||
|
||||
// Acquires a frame from swapchain, also calls poll_input() after acquire
|
||||
// since acquire tends to block.
|
||||
bool begin_frame();
|
||||
// Presents and iterates frame context.
|
||||
// Present is skipped if swapchain resource was not touched.
|
||||
// The normal app loop is something like begin_frame() -> submit work -> end_frame().
|
||||
bool end_frame();
|
||||
|
||||
// For external swapchains we don't have a normal acquire -> present cycle.
|
||||
// - set_external_frame()
|
||||
// - index replaces the acquire next image index.
|
||||
// - acquire_semaphore replaces semaphore from acquire next image.
|
||||
// - frame_time controls the frame time passed down.
|
||||
// - begin_frame()
|
||||
// - submit work
|
||||
// - end_frame()
|
||||
// - consume_external_release_semaphore()
|
||||
// - Returns the release semaphore that can passed to the equivalent of QueuePresentKHR.
|
||||
void set_external_frame(unsigned index, Semaphore acquire_semaphore, double frame_time);
|
||||
Semaphore consume_external_release_semaphore();
|
||||
|
||||
CommandBuffer::Type get_current_present_queue_type() const;
|
||||
|
||||
// Equivalent to calling destructor.
|
||||
void teardown();
|
||||
|
||||
WSIPlatform &get_platform()
|
||||
{
|
||||
VK_ASSERT(platform);
|
||||
return *platform;
|
||||
}
|
||||
|
||||
// For Android. Used in response to APP_CMD_{INIT,TERM}_WINDOW once
|
||||
// we have a proper swapchain going.
|
||||
// We have to completely drain swapchain before the window is terminated on Android.
|
||||
void deinit_surface_and_swapchain();
|
||||
void reinit_surface_and_swapchain(VkSurfaceKHR new_surface);
|
||||
|
||||
void set_window_title(const std::string &title);
|
||||
|
||||
double get_smooth_frame_time() const;
|
||||
double get_smooth_elapsed_time() const;
|
||||
|
||||
private:
|
||||
void update_framebuffer(unsigned width, unsigned height);
|
||||
|
||||
ContextHandle context;
|
||||
VkSurfaceKHR surface = VK_NULL_HANDLE;
|
||||
VkSwapchainKHR swapchain = VK_NULL_HANDLE;
|
||||
std::vector<VkImage> swapchain_images;
|
||||
std::vector<Semaphore> release_semaphores;
|
||||
DeviceHandle device;
|
||||
const VolkDeviceTable *table = nullptr;
|
||||
|
||||
unsigned swapchain_width = 0;
|
||||
unsigned swapchain_height = 0;
|
||||
float swapchain_aspect_ratio = 1.0f;
|
||||
VkSurfaceFormatKHR swapchain_surface_format = { VK_FORMAT_UNDEFINED, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
|
||||
PresentMode current_present_mode = PresentMode::SyncToVBlank;
|
||||
PresentMode present_mode = PresentMode::SyncToVBlank;
|
||||
bool low_latency_mode_enable = false;
|
||||
|
||||
VkPresentModeKHR active_present_mode = VK_PRESENT_MODE_FIFO_KHR;
|
||||
std::vector<VkPresentModeKHR> present_mode_compat_group;
|
||||
bool update_active_presentation_mode(PresentMode mode);
|
||||
|
||||
VkImageUsageFlags current_extra_usage = 0;
|
||||
VkImageUsageFlags extra_usage = 0;
|
||||
bool swapchain_is_suboptimal = false;
|
||||
|
||||
enum class SwapchainError
|
||||
{
|
||||
None,
|
||||
NoSurface,
|
||||
Error
|
||||
};
|
||||
SwapchainError init_swapchain(unsigned width, unsigned height);
|
||||
bool blocking_init_swapchain(unsigned width, unsigned height);
|
||||
|
||||
uint32_t swapchain_index = 0;
|
||||
bool has_acquired_swapchain_index = false;
|
||||
|
||||
WSIPlatform *platform = nullptr;
|
||||
|
||||
std::vector<ImageHandle> external_swapchain_images;
|
||||
|
||||
unsigned external_frame_index = 0;
|
||||
Semaphore external_acquire;
|
||||
Semaphore external_release;
|
||||
bool frame_is_external = false;
|
||||
|
||||
BackbufferFormat backbuffer_format = BackbufferFormat::sRGB;
|
||||
BackbufferFormat current_backbuffer_format = BackbufferFormat::sRGB;
|
||||
|
||||
bool support_prerotate = false;
|
||||
VkSurfaceTransformFlagBitsKHR swapchain_current_prerotate = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
|
||||
bool begin_frame_external();
|
||||
double external_frame_time = 0.0;
|
||||
|
||||
double smooth_frame_time = 0.0;
|
||||
double smooth_elapsed_time = 0.0;
|
||||
|
||||
uint64_t present_id = 0;
|
||||
uint64_t present_last_id = 0;
|
||||
unsigned present_frame_latency = 0;
|
||||
|
||||
void tear_down_swapchain();
|
||||
void drain_swapchain(bool in_tear_down);
|
||||
void wait_swapchain_latency();
|
||||
|
||||
VkHdrMetadataEXT hdr_metadata = { VK_STRUCTURE_TYPE_HDR_METADATA_EXT };
|
||||
|
||||
struct DeferredDeletion
|
||||
{
|
||||
VkSwapchainKHR swapchain;
|
||||
Fence fence;
|
||||
};
|
||||
Util::SmallVector<DeferredDeletion> deferred_swapchains;
|
||||
Vulkan::Fence last_present_fence;
|
||||
void nonblock_delete_swapchains();
|
||||
|
||||
VkSurfaceFormatKHR find_suitable_present_format(const std::vector<VkSurfaceFormatKHR> &formats, BackbufferFormat desired_format) const;
|
||||
|
||||
#ifdef HAVE_WSI_DXGI_INTEROP
|
||||
std::unique_ptr<DXGIInteropSwapchain> dxgi;
|
||||
bool init_surface_swapchain_dxgi(unsigned width, unsigned height);
|
||||
bool begin_frame_dxgi();
|
||||
bool end_frame_dxgi();
|
||||
#endif
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user