Merge remote-tracking branch 'origin/master' into claude/issue-61
# Conflicts: # interfaces/Crafter.Graphics-Device.cppm # interfaces/Crafter.Graphics-VulkanBuffer.cppm # project.cpp
This commit is contained in:
commit
1f12f074b1
11 changed files with 860 additions and 9 deletions
|
|
@ -169,6 +169,27 @@ export namespace Crafter {
|
|||
// creation; defaults to 1 so the rounding math is well-defined before then.
|
||||
inline static VkDeviceSize nonCoherentAtomSize = 1;
|
||||
|
||||
// Core physical-device properties, captured once at Initialize from the
|
||||
// VkPhysicalDeviceProperties2 query. Kept because the pipeline-cache
|
||||
// persistence path needs vendorID / deviceID / pipelineCacheUUID to
|
||||
// decide whether an on-disk blob was written by this exact device.
|
||||
inline static VkPhysicalDeviceProperties deviceProperties = {};
|
||||
|
||||
// One process-wide pipeline cache fed to every vkCreate*Pipelines call
|
||||
// (compute UI/user shaders + RT pipelines). Pipeline compilation is a
|
||||
// one-time cold-start cost, not a per-frame one; a single shared cache
|
||||
// lets the driver reuse compiled shader binaries across the several
|
||||
// pipelines built at startup and — once persisted to disk (see
|
||||
// LoadPipelineCache / SavePipelineCache) — across runs. VK_NULL_HANDLE
|
||||
// until LoadPipelineCache runs; passing VK_NULL_HANDLE to a create call
|
||||
// is valid and simply means "no cache", so the create sites need no
|
||||
// null check.
|
||||
inline static VkPipelineCache pipelineCache = VK_NULL_HANDLE;
|
||||
// Where the serialized cache lives. Relative to the working directory,
|
||||
// matching the gpu_crash_dump-* convention. Set before Initialize to
|
||||
// relocate it.
|
||||
inline static std::filesystem::path pipelineCachePath = "pipeline_cache.bin";
|
||||
|
||||
inline static VkPhysicalDeviceDescriptorHeapPropertiesEXT descriptorHeapProperties = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_HEAP_PROPERTIES_EXT
|
||||
};
|
||||
|
|
@ -207,6 +228,23 @@ export namespace Crafter {
|
|||
// ComputeShader read the offset off the pipeline they record.
|
||||
|
||||
static void CheckVkResult(VkResult result);
|
||||
|
||||
// ─── Pipeline cache persistence (issue #69) ─────────────────────
|
||||
// LoadPipelineCache creates `pipelineCache`, seeding it from
|
||||
// pipelineCachePath when the file exists *and* its header matches this
|
||||
// device (PipelineCacheDataCompatible) — a stale or foreign blob is
|
||||
// discarded so the driver never rejects it. Called once from
|
||||
// Initialize. SavePipelineCache writes the driver's current cache blob
|
||||
// back out; Initialize registers it with std::atexit so the cache is
|
||||
// serialized at process shutdown without any explicit teardown call.
|
||||
static void LoadPipelineCache();
|
||||
static void SavePipelineCache();
|
||||
// True when `data` is a VkPipelineCache blob whose header was written by
|
||||
// a device with the same vendorID / deviceID / pipelineCacheUUID as
|
||||
// `deviceProperties`. Pure logic over the standard 32-byte cache header,
|
||||
// so it is driven directly by the PipelineCacheValidation test with no
|
||||
// GPU device. A blob too short to hold the header is incompatible.
|
||||
static bool PipelineCacheDataCompatible(std::span<const std::byte> data);
|
||||
// Selects a memory type index from typeBits that satisfies `required`.
|
||||
// When `preferred` bits are also given, a type satisfying both is
|
||||
// chosen first; if none exists we fall back to required-only rather
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ export namespace Crafter {
|
|||
.layout = VK_NULL_HANDLE
|
||||
};
|
||||
|
||||
Device::CheckVkResult(Device::vkCreateRayTracingPipelinesKHR(Device::device, {}, {}, 1, &rtPipelineInfo, nullptr, &pipeline));
|
||||
Device::CheckVkResult(Device::vkCreateRayTracingPipelinesKHR(Device::device, {}, Device::pipelineCache, 1, &rtPipelineInfo, nullptr, &pipeline));
|
||||
|
||||
std::size_t dataSize = Device::rayTracingProperties.shaderGroupHandleSize * rtPipelineInfo.groupCount;
|
||||
shaderHandles.resize(dataSize);
|
||||
|
|
|
|||
|
|
@ -65,6 +65,12 @@ namespace Crafter {
|
|||
// COHERENT bit on a coherent type (and vice versa), so the flush/
|
||||
// invalidate paths gate on this recorded value, never on the request.
|
||||
VkMemoryPropertyFlags memoryPropertyFlagsChosen = 0;
|
||||
// Byte capacity the buffer was created with, and the usage flags it was
|
||||
// created with. Resize reuses the allocation in place when a new
|
||||
// request still fits within `capacity` and these immutable-at-create
|
||||
// properties match, avoiding a destroy+reallocate.
|
||||
std::uint32_t capacity = 0;
|
||||
VkBufferUsageFlags2 usageFlagsCreated = 0;
|
||||
};
|
||||
|
||||
export template<typename T>
|
||||
|
|
@ -92,6 +98,8 @@ namespace Crafter {
|
|||
// available on every device — see Device::GetMemoryType.
|
||||
void Create(VkBufferUsageFlags2 usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, std::uint32_t count, VkMemoryPropertyFlags preferredPropertyFlags = 0) {
|
||||
size = count * sizeof(T);
|
||||
capacity = size;
|
||||
usageFlagsCreated = usageFlags;
|
||||
|
||||
// Carry usage in the maintenance5 flags2 chain so 64-bit bits
|
||||
// (e.g. VK_BUFFER_USAGE_2_MEMORY_DECOMPRESSION_BIT_EXT, bit 35)
|
||||
|
|
@ -148,6 +156,22 @@ namespace Crafter {
|
|||
}
|
||||
|
||||
void Resize(VkBufferUsageFlags2 usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, std::uint32_t count, VkMemoryPropertyFlags preferredPropertyFlags = 0) {
|
||||
// Reuse the existing allocation in place when the request still fits
|
||||
// and the fixed-at-create properties match: usage flags are
|
||||
// immutable after creation, and the memory type already chosen must
|
||||
// still satisfy the required property flags. preferredPropertyFlags
|
||||
// is a best-effort perf hint and does not affect correctness, so it
|
||||
// is intentionally not part of the guard. The buffer handle (and its
|
||||
// device address / mapped pointer) is preserved, only `size` shrinks
|
||||
// to the new logical extent.
|
||||
std::uint32_t requestedSize = count * sizeof(T);
|
||||
if(buffer != VK_NULL_HANDLE
|
||||
&& requestedSize <= capacity
|
||||
&& usageFlags == usageFlagsCreated
|
||||
&& (memoryPropertyFlagsChosen & memoryPropertyFlags) == memoryPropertyFlags) {
|
||||
size = requestedSize;
|
||||
return;
|
||||
}
|
||||
if(buffer != VK_NULL_HANDLE) {
|
||||
Clear();
|
||||
}
|
||||
|
|
@ -275,6 +299,8 @@ namespace Crafter {
|
|||
memory = other.memory;
|
||||
size = other.size;
|
||||
mappedSize = other.mappedSize;
|
||||
capacity = other.capacity;
|
||||
usageFlagsCreated = other.usageFlagsCreated;
|
||||
memoryPropertyFlagsChosen = other.memoryPropertyFlagsChosen;
|
||||
other.buffer = VK_NULL_HANDLE;
|
||||
address = other.address;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue