Merge pull request #2661 from adriadam10/feat/fixed-push-slots
perf(agc): give each graphics stage a fixed push constant slot with pipeline libraries
This commit is contained in:
commit
197ffac972
|
|
@ -165,6 +165,7 @@ public:
|
|||
bool ConservativeRasterization() const;
|
||||
VkShaderStageFlags SubgroupStages() const;
|
||||
bool ProvokingVertexLast() const;
|
||||
bool GraphicsPipelineLibraries() const;
|
||||
// A presentation is a few steps so the presenter holds GuestMemory::GpuMutex only while it
|
||||
// touches the queue. Presentations are slots (FlipInFlight() + 1, each with its own command
|
||||
// buffer, fence, kept resident image and dump buffer): RetirePresents(keep) (no mutex) retires
|
||||
|
|
|
|||
|
|
@ -187,6 +187,7 @@ DrawVerdict Driver::draw(QueueState& queue, std::span<const std::uint32_t> packe
|
|||
for (std::size_t i = 0; i < programs.size(); ++i) {
|
||||
if (roles[i] == Role::GeometryBack) continue;
|
||||
const auto& program = programs[i];
|
||||
pushCursorBytes = Graphics::StagePushOffset(pushCursorBytes, program.binary.stage, localDevice->GraphicsPipelineLibraries());
|
||||
pushOffsets[i] = pushCursorBytes;
|
||||
if (drawHit) {
|
||||
|
||||
|
|
@ -204,7 +205,8 @@ DrawVerdict Driver::draw(QueueState& queue, std::span<const std::uint32_t> packe
|
|||
} else if (i == 0) {
|
||||
fold(result, drawParameters);
|
||||
}
|
||||
require(result.pushConstants.size() <= Graphics::PipelinePushConstantBytes - pushCursorBytes, "stage push constants exceed the pipeline push constant block");
|
||||
const auto slotEnd = (pushCursorBytes / Graphics::PipelinePushSlotBytes + 1u) * Graphics::PipelinePushSlotBytes;
|
||||
require(result.pushConstants.size() <= slotEnd - pushCursorBytes, "stage push constants exceed the pipeline push constant block");
|
||||
stages.push_back({program.binary.stage, &result, result.pushConstants.empty() ? 0u : pushCursorBytes});
|
||||
pushCursorBytes += static_cast<std::uint32_t>(result.pushConstants.size());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ ShaderRecompiler::RecompileResult Driver::materializeDrawStage(std::size_t i, st
|
|||
program.binary,
|
||||
{waveSize, program.firstUserSgpr, program.userData, std::nullopt, program.binary.stage == Stage::Fragment ? std::optional(pixel) : std::nullopt, vertexInfos[i], memory, RegisteredFloatMode(*program.snapshot)},
|
||||
localDevice->Target(),
|
||||
{0, 0, pushOffset, (graphics.stages.mesh ? ShaderRecompiler::MeshDrawPushOffsetBytes : Graphics::PipelinePushConstantBytes) - pushOffset},
|
||||
{0, 0, pushOffset, (graphics.stages.mesh ? ShaderRecompiler::MeshDrawPushOffsetBytes : Graphics::PipelinePushSlotBytes) - pushOffset % Graphics::PipelinePushSlotBytes},
|
||||
ShaderRecompiler::GraphicsCompileContext{program.firstUserSgpr, linked, graphics.stages.mesh, graphics.stages.tessellation, {drawParameters.indexAddress, drawParameters.indexCount, drawParameters.indexSize, drawParameters.instanceCount}}
|
||||
};
|
||||
const auto waitedBefore = traceCapSync() || profile ? Graphics::Recorder::ThreadWaitedMs() : 0.0;
|
||||
|
|
|
|||
|
|
@ -490,6 +490,7 @@ std::unique_ptr<RegisteredPreparation> PlanRegistered(const ShaderSnapshot& snap
|
|||
plan->memory = {{{snapshot.codeAddress, std::as_bytes(std::span(snapshot.code))}, {snapshot.headerAddress, snapshot.header}}};
|
||||
auto& request = plan->request.emplace(ShaderRecompiler::RecompileRequest{{stage, address, code, snapshot.headerAddress, snapshot.header}, {wave, firstUser, plan->userData, compute, pixel, vertex, plan->memory, RegisteredFloatMode(snapshot)}, stage == Stage::Compute ? device.ComputeTarget(wave) : device.Target(), {0, 0, 0, 128}, graphics});
|
||||
if (graphics && graphics->mesh) request.layout.pushConstantSizeBytes = ShaderRecompiler::MeshDrawPushOffsetBytes;
|
||||
if (pixel && device.GraphicsPipelineLibraries()) request.layout.pushConstantOffsetBytes = Graphics::FixedPushOffset(Stage::Fragment);
|
||||
return plan;
|
||||
}
|
||||
|
||||
|
|
@ -629,7 +630,8 @@ std::vector<PreparedGraphicsStage> PrepareGraphicsStages(const DrawDecode& decod
|
|||
}
|
||||
std::vector<PreparedGraphicsStage> prepared;
|
||||
std::uint32_t pushOffset = 0;
|
||||
const auto capacity = decoded.state.stages.mesh ? ShaderRecompiler::MeshDrawPushOffsetBytes : Graphics::PipelinePushConstantBytes;
|
||||
const bool fixedSlots = target.fixedPushSlots;
|
||||
const auto capacity = decoded.state.stages.mesh ? ShaderRecompiler::MeshDrawPushOffsetBytes : Graphics::PipelinePushSlotBytes;
|
||||
for (std::size_t index = 0; index < decoded.programs.size(); ++index) {
|
||||
if (decoded.roles[index] == ShaderRecompiler::ProgramRole::GeometryBack) continue;
|
||||
const auto& program = decoded.programs[index];
|
||||
|
|
@ -637,7 +639,10 @@ std::vector<PreparedGraphicsStage> PrepareGraphicsStages(const DrawDecode& decod
|
|||
const auto wave = fragment ? decoded.state.stages.fragmentWaveSize : decoded.state.stages.vertexWaveSize;
|
||||
std::optional<ShaderRecompiler::ShaderVertexStageInfo> vertex;
|
||||
if (!fragment) vertex = Graphics::DecodeVertexStageInfo(program.binary.header, program.binary.headerAddress, program.userData, nullptr, true);
|
||||
ShaderRecompiler::RecompileRequest request{program.binary, {wave, program.firstUserSgpr, program.userData, {}, fragment ? std::optional(decoded.pixel) : std::nullopt, vertex, memory, RegisteredFloatMode(*program.snapshot)}, target, {0, 0, pushOffset, capacity - pushOffset}, ShaderRecompiler::GraphicsCompileContext{program.firstUserSgpr, linked, decoded.state.stages.mesh, decoded.state.stages.tessellation, {}}};
|
||||
pushOffset = Graphics::StagePushOffset(pushOffset, program.binary.stage, fixedSlots);
|
||||
const auto offset = pushOffset;
|
||||
const auto room = (fixedSlots && fragment ? Graphics::PipelinePushSlotBytes : capacity) - pushOffset % Graphics::PipelinePushSlotBytes;
|
||||
ShaderRecompiler::RecompileRequest request{program.binary, {wave, program.firstUserSgpr, program.userData, {}, fragment ? std::optional(decoded.pixel) : std::nullopt, vertex, memory, RegisteredFloatMode(*program.snapshot)}, target, {0, 0, offset, room}, ShaderRecompiler::GraphicsCompileContext{program.firstUserSgpr, linked, decoded.state.stages.mesh, decoded.state.stages.tessellation, {}}};
|
||||
std::vector<std::uint64_t> key;
|
||||
ShaderRecompiler::BuildPreparedShaderKey(request, key);
|
||||
std::shared_ptr<const ShaderRecompiler::SourceHandle> handle;
|
||||
|
|
@ -652,7 +657,7 @@ std::vector<PreparedGraphicsStage> PrepareGraphicsStages(const DrawDecode& decod
|
|||
handle = PrepareShaderWithDiagnostics(request);
|
||||
}
|
||||
const auto bytes = handle->artifact->bindings.pushConstantSizeBytes;
|
||||
require(bytes <= capacity - pushOffset, "prepared graphics stages exceed the push constant block");
|
||||
require(bytes <= room, "prepared graphics stages exceed the push constant block");
|
||||
prepared.push_back({program.snapshot, {program.codeOffset, handle}});
|
||||
pushOffset += bytes;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1044,7 +1044,7 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window) : state(std::make_u
|
|||
VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT libraryProperties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_PROPERTIES_EXT};
|
||||
VkPhysicalDeviceProperties2 properties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, &libraryProperties};
|
||||
state->InstanceFunction<PFN_vkGetPhysicalDeviceProperties2>("vkGetPhysicalDeviceProperties2")(selected, &properties);
|
||||
state->graphicsPipelineLibrary = libraryFeatures.graphicsPipelineLibrary == VK_TRUE && dynamicStateFeatures.extendedDynamicState == VK_TRUE && renderingFeatures.dynamicRendering == VK_TRUE && libraryProperties.graphicsPipelineLibraryFastLinking == VK_TRUE;
|
||||
state->graphicsPipelineLibrary = libraryFeatures.graphicsPipelineLibrary == VK_TRUE && dynamicStateFeatures.extendedDynamicState == VK_TRUE && renderingFeatures.dynamicRendering == VK_TRUE && libraryProperties.graphicsPipelineLibraryFastLinking == VK_TRUE && properties.properties.limits.maxPushConstantsSize >= Graphics::PipelinePushConstantBytes;
|
||||
if (state->graphicsPipelineLibrary) deviceExtensions.insert(deviceExtensions.end(), libraryExtensions.begin(), libraryExtensions.end());
|
||||
}
|
||||
renderingFeatures = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR, nullptr, VK_TRUE};
|
||||
|
|
@ -2623,6 +2623,7 @@ ShaderRecompiler::SpirvTarget VulkanDevice::buildTarget() const {
|
|||
target.fragmentShaderBarycentricEnabled = state->fragmentShaderBarycentric;
|
||||
target.nonConstantImageOffsets = state->maintenance8;
|
||||
target.narrowSubgroupClock = state->narrowSubgroupClock;
|
||||
target.fixedPushSlots = state->graphicsPipelineLibrary;
|
||||
target.srgbDecodeFormats = state->srgbDecodeFormats;
|
||||
if (state->meshShader) {
|
||||
const auto& mesh = state->meshLimits;
|
||||
|
|
@ -2662,6 +2663,10 @@ bool VulkanDevice::ProvokingVertexLast() const {
|
|||
return state->provokingVertexLast;
|
||||
}
|
||||
|
||||
bool VulkanDevice::GraphicsPipelineLibraries() const {
|
||||
return state->graphicsPipelineLibrary;
|
||||
}
|
||||
|
||||
Graphics::Context VulkanDevice::graphicsContext() const {
|
||||
static const bool noCache = std::getenv("APS5_NO_CONTEXT_CACHE") != nullptr;
|
||||
if (state->contextReady && !noCache) return state->context;
|
||||
|
|
@ -3526,7 +3531,7 @@ void VulkanDevice::recordDispatch(RecordedDispatch& record) {
|
|||
recorder.Keep(std::move(memory));
|
||||
}
|
||||
if (record.pushStages != 0) {
|
||||
context.Resolved(&Graphics::DeviceFunctions::cmdPushConstants, "vkCmdPushConstants")(commands, record.objects->layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, Graphics::PipelinePushConstantBytes, record.pushBytes->data());
|
||||
context.Resolved(&Graphics::DeviceFunctions::cmdPushConstants, "vkCmdPushConstants")(commands, record.objects->layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, Graphics::ComputePushConstantBytes, record.pushBytes->data());
|
||||
}
|
||||
const auto gpuTiming = recorder.BeginGpuTiming(record.programAddress != 0 ? record.programAddress : record.shader->program->variantId);
|
||||
if (argumentImport != nullptr) context.Resolved(&Graphics::DeviceFunctions::cmdDispatchIndirect, "vkCmdDispatchIndirect")(commands, argumentImport->buffer, arguments - argumentImport->base);
|
||||
|
|
@ -3572,7 +3577,7 @@ VulkanDevice::IndirectOutcome VulkanDevice::dispatch(const ShaderRecompiler::Rec
|
|||
}
|
||||
const std::array<Graphics::CompiledShader, 1> shaders{{{ShaderRecompiler::ShaderStage::Compute, &shader, 0}}};
|
||||
const auto pushStages = Graphics::PushConstantStages(shaders);
|
||||
if (pushStages != 0 && state->properties.limits.maxPushConstantsSize < Graphics::PipelinePushConstantBytes) {
|
||||
if (pushStages != 0 && state->properties.limits.maxPushConstantsSize < Graphics::ComputePushConstantBytes) {
|
||||
throw std::runtime_error("Vulkan dispatch: compute push constant range exceeds device limit");
|
||||
}
|
||||
auto pushBytes = Graphics::AssemblePushConstants(shaders);
|
||||
|
|
@ -3739,7 +3744,7 @@ VulkanDevice::IndirectOutcome VulkanDevice::dispatch(const ShaderRecompiler::Rec
|
|||
check(state->DeviceFunction<PFN_vkCreateShaderModule>("vkCreateShaderModule")(state->device, &moduleInfo, nullptr, &objects->module), "vkCreateShaderModule");
|
||||
timing.Mark("validate_shader_module");
|
||||
const std::array<VkDescriptorSetLayout, 2> setLayouts{resources->Layout(), shader.workgroupMemoryDwords != 0 ? state->workgroupMemoryLayout : VK_NULL_HANDLE};
|
||||
const VkPushConstantRange push{VK_SHADER_STAGE_COMPUTE_BIT, 0, Graphics::PipelinePushConstantBytes};
|
||||
const VkPushConstantRange push{VK_SHADER_STAGE_COMPUTE_BIT, 0, Graphics::ComputePushConstantBytes};
|
||||
VkPipelineLayoutCreateInfo layoutInfo{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
|
||||
layoutInfo.setLayoutCount = shader.workgroupMemoryDwords != 0 ? ShaderRecompiler::WorkgroupMemoryDescriptorSet + 1 : 1;
|
||||
layoutInfo.pSetLayouts = setLayouts.data();
|
||||
|
|
|
|||
|
|
@ -9,7 +9,29 @@
|
|||
|
||||
namespace AgcDriver::Graphics {
|
||||
|
||||
inline constexpr std::uint32_t PipelinePushConstantBytes = 128;
|
||||
inline constexpr std::uint32_t PipelinePushConstantBytes = 256;
|
||||
inline constexpr std::uint32_t PipelinePushSlotBytes = 128;
|
||||
inline constexpr std::uint32_t ComputePushConstantBytes = 128;
|
||||
|
||||
inline std::uint32_t PushBlockBytes(bool libraries) {
|
||||
return libraries ? PipelinePushConstantBytes : PipelinePushSlotBytes;
|
||||
}
|
||||
|
||||
inline VkShaderStageFlags PreRasterizationPushStages(bool meshShader, bool tessellation, bool geometry) {
|
||||
VkShaderStageFlags stages = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
if (tessellation) stages |= VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
|
||||
if (geometry) stages |= VK_SHADER_STAGE_GEOMETRY_BIT;
|
||||
if (meshShader) stages |= VK_SHADER_STAGE_MESH_BIT_EXT;
|
||||
return stages;
|
||||
}
|
||||
|
||||
inline std::uint32_t FixedPushOffset(ShaderRecompiler::ShaderStage stage) {
|
||||
return stage == ShaderRecompiler::ShaderStage::Fragment ? PipelinePushSlotBytes : 0u;
|
||||
}
|
||||
|
||||
inline std::uint32_t StagePushOffset(std::uint32_t cursor, ShaderRecompiler::ShaderStage stage, bool fixedSlots) {
|
||||
return fixedSlots && stage == ShaderRecompiler::ShaderStage::Fragment ? PipelinePushSlotBytes : cursor;
|
||||
}
|
||||
|
||||
struct CompiledShader {
|
||||
ShaderRecompiler::ShaderStage stage;
|
||||
|
|
@ -65,6 +87,7 @@ inline std::array<std::byte, PipelinePushConstantBytes> AssemblePushConstants(st
|
|||
if (bytes.empty()) continue;
|
||||
Require(bytes.size() % 4 == 0 && shader.pushConstantOffset % 4 == 0, "shader push constant range is not DWORD aligned");
|
||||
Require(shader.pushConstantOffset < PipelinePushConstantBytes && bytes.size() <= PipelinePushConstantBytes - shader.pushConstantOffset, "shader push constant range lies outside the pipeline push constant block");
|
||||
Require(bytes.size() <= PipelinePushSlotBytes - shader.pushConstantOffset % PipelinePushSlotBytes, "shader push constant range crosses a push constant slot");
|
||||
for (std::size_t i = 0; i < bytes.size(); ++i) {
|
||||
const auto position = shader.pushConstantOffset + i;
|
||||
Require(!occupied[position], "shader push constant ranges of different stages overlap");
|
||||
|
|
|
|||
|
|
@ -1494,7 +1494,7 @@ void pushDrawConstants(const Pipeline& pipeline, VkCommandBuffer commands, const
|
|||
const auto firstVertex = draw.indirect ? draw.indirect->vertexConstant : draw.firstVertex;
|
||||
const auto firstInstance = draw.indirect ? draw.indirect->instanceConstant : draw.firstInstance;
|
||||
const std::array<std::uint32_t, ShaderRecompiler::MeshDrawPushBytes / 4> words{draw.indexCount, firstVertex, firstInstance, draw.indexed ? draw.indexSize : 0u, static_cast<std::uint32_t>(meshArguments), static_cast<std::uint32_t>(meshArguments >> 32u)};
|
||||
static_assert(ShaderRecompiler::MeshDrawPushOffsetBytes + ShaderRecompiler::MeshDrawPushBytes == PipelinePushConstantBytes);
|
||||
static_assert(ShaderRecompiler::MeshDrawPushOffsetBytes + ShaderRecompiler::MeshDrawPushBytes == PipelinePushSlotBytes);
|
||||
std::memcpy(block.data() + ShaderRecompiler::MeshDrawPushOffsetBytes, words.data(), sizeof(words));
|
||||
pipeline.PushConstants(commands, stages | VK_SHADER_STAGE_MESH_BIT_EXT, block);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,6 +117,10 @@ void ValidateViewport(const Context& context, const VkViewport& viewport) {
|
|||
|
||||
namespace {
|
||||
|
||||
VkShaderStageFlags pushStagesOf(const Context& context) {
|
||||
return PreRasterizationPushStages(context.meshShader, context.tessellationShader, context.geometryShader);
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
void appendKey(std::vector<std::byte>& key, const TValue& value) {
|
||||
static_assert(std::is_trivially_copyable_v<TValue>);
|
||||
|
|
@ -165,7 +169,7 @@ bool libraryKeys(PipelineLibraryKeys& keys, const Context& context, const State&
|
|||
}
|
||||
appendKey(keys.renderPass, state.depth.has_value());
|
||||
if (state.depth) appendKey(keys.renderPass, state.depth->format);
|
||||
appendKey(keys.layout, PushConstantStages(shaders));
|
||||
appendKey(keys.layout, pushStagesOf(context));
|
||||
for (const auto word : resources.LayoutKey()) appendKey(keys.layout, word);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -200,7 +204,10 @@ Pipeline::Pipeline(const Context& context, const State& state, const VertexInput
|
|||
Require(mesh.maxVertices <= context.meshLimits.maxMeshOutputVertices && mesh.maxPrimitives <= context.meshLimits.maxMeshOutputPrimitives && static_cast<std::uint64_t>(mesh.ldsSizeDwords) * 4 <= context.meshLimits.maxMeshSharedMemorySize, "mesh output or LDS exceeds device limits");
|
||||
}
|
||||
const auto pushStages = PushConstantStages(shaders);
|
||||
Require(pushStages == 0 || context.limits.maxPushConstantsSize >= PipelinePushConstantBytes, "graphics push constant range exceeds device limit");
|
||||
Require(pushStages == 0 || context.limits.maxPushConstantsSize >= PushBlockBytes(dynamicRendering), "graphics push constant range exceeds device limit");
|
||||
if (dynamicRendering) {
|
||||
for (const auto& shader : shaders) Require(shader.program->pushConstants.empty() || (shader.pushConstantOffset >= PipelinePushSlotBytes) == (shader.stage == ShaderRecompiler::ShaderStage::Fragment), "a stage's push constants lie outside its push constant slot");
|
||||
}
|
||||
try {
|
||||
std::vector<VkPipelineShaderStageCreateInfo> stages(shaders.size());
|
||||
std::vector<PipelineSpecialization> specializations;
|
||||
|
|
@ -222,12 +229,13 @@ Pipeline::Pipeline(const Context& context, const State& state, const VertexInput
|
|||
// A descriptor set layout with the same bindings as this one is compatible with the pipeline
|
||||
// layout, so later draws bind their own ShaderResources' set under it.
|
||||
const auto setLayout = resources.Layout();
|
||||
const VkPushConstantRange push{pushStages, 0, PipelinePushConstantBytes};
|
||||
const VkPushConstantRange push{pushStages, 0, PipelinePushSlotBytes};
|
||||
const std::array<VkPushConstantRange, 2> slots{{{pushStagesOf(context), 0, PipelinePushSlotBytes}, {VK_SHADER_STAGE_FRAGMENT_BIT, PipelinePushSlotBytes, PipelinePushSlotBytes}}};
|
||||
VkPipelineLayoutCreateInfo layoutInfo{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
|
||||
layoutInfo.setLayoutCount = 1;
|
||||
layoutInfo.pSetLayouts = &setLayout;
|
||||
layoutInfo.pushConstantRangeCount = pushStages != 0 ? 1 : 0;
|
||||
layoutInfo.pPushConstantRanges = pushStages != 0 ? &push : nullptr;
|
||||
layoutInfo.pushConstantRangeCount = dynamicRendering ? static_cast<std::uint32_t>(slots.size()) : pushStages != 0 ? 1 : 0;
|
||||
layoutInfo.pPushConstantRanges = dynamicRendering ? slots.data() : pushStages != 0 ? &push : nullptr;
|
||||
Check(context.Function<PFN_vkCreatePipelineLayout>("vkCreatePipelineLayout")(context.device, &layoutInfo, nullptr, &layout), "vkCreatePipelineLayout graphics");
|
||||
std::vector<VkAttachmentDescription> colors;
|
||||
std::vector<VkAttachmentReference> references(state.blends.size(), VkAttachmentReference{VK_ATTACHMENT_UNUSED, attachmentLayout});
|
||||
|
|
@ -536,7 +544,12 @@ void Pipeline::ContinueBackFaces(VkCommandBuffer commands, const State& state) c
|
|||
|
||||
void Pipeline::PushConstants(VkCommandBuffer commands, VkShaderStageFlags stages, std::span<const std::byte, PipelinePushConstantBytes> bytes) const {
|
||||
if (stages == 0) return;
|
||||
context.Resolved(&DeviceFunctions::cmdPushConstants, "vkCmdPushConstants")(commands, layout, stages, 0, PipelinePushConstantBytes, bytes.data());
|
||||
if (dynamicRendering) {
|
||||
context.Resolved(&DeviceFunctions::cmdPushConstants, "vkCmdPushConstants")(commands, layout, pushStagesOf(context), 0, PipelinePushSlotBytes, bytes.data());
|
||||
context.Resolved(&DeviceFunctions::cmdPushConstants, "vkCmdPushConstants")(commands, layout, VK_SHADER_STAGE_FRAGMENT_BIT, PipelinePushSlotBytes, PipelinePushSlotBytes, bytes.data() + PipelinePushSlotBytes);
|
||||
return;
|
||||
}
|
||||
context.Resolved(&DeviceFunctions::cmdPushConstants, "vkCmdPushConstants")(commands, layout, stages, 0, PipelinePushSlotBytes, bytes.data());
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
|
|||
|
|
@ -478,11 +478,11 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi
|
|||
push = true;
|
||||
Require(type.size() == 3 && module.decorations[typeId].block, "push constant variable must be a Block struct with exactly one member");
|
||||
const auto offset = module.offsets.find({typeId, 0});
|
||||
Require(offset != module.offsets.end() && offset->second == 0, "push constant member must have offset zero");
|
||||
Require(offset != module.offsets.end() && offset->second % PipelinePushSlotBytes == 0 && offset->second < PipelinePushConstantBytes, "push constant member must start at a stage slot");
|
||||
const auto& array = module.Type(type[2]);
|
||||
Require(array.size() == 4 && (array[0] & 0xffffu) == spv::OpTypeArray && module.Signature(array[2]) == "u32", "push constant member must be an array of u32");
|
||||
const auto length = module.constants.find(array[3]);
|
||||
Require(length != module.constants.end() && length->second == PipelinePushConstantBytes / 4, "push constant array must contain 32 elements");
|
||||
Require(length != module.constants.end() && length->second == PipelinePushSlotBytes / 4, "push constant array must contain 32 elements");
|
||||
const auto stride = module.decorations[type[2]].stride;
|
||||
Require(stride.has_value() && *stride == 4, "push constant array must have an ArrayStride of 4");
|
||||
} else if (variable.storage == spv::StorageClassWorkgroup) {
|
||||
|
|
|
|||
|
|
@ -1918,11 +1918,16 @@ void pushConstantTests() {
|
|||
std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{{ShaderRecompiler::ShaderStage::Vertex, &vertex, 0}, {ShaderRecompiler::ShaderStage::Fragment, &fragment, 8}}};
|
||||
Require(AgcDriver::Graphics::PushConstantStages(shaders) == (VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT), "push constant stage union changed");
|
||||
const auto bytes = AgcDriver::Graphics::AssemblePushConstants(shaders);
|
||||
Require(bytes.size() == 128 && bytes[0] == std::byte{1} && bytes[7] == std::byte{1} && bytes[8] == std::byte{2} && bytes[19] == std::byte{2} && bytes[20] == std::byte{0} && bytes[127] == std::byte{0}, "assembled push constants are misplaced");
|
||||
Require(bytes.size() == AgcDriver::Graphics::PipelinePushConstantBytes && bytes[0] == std::byte{1} && bytes[7] == std::byte{1} && bytes[8] == std::byte{2} && bytes[19] == std::byte{2} && bytes[20] == std::byte{0} && bytes[127] == std::byte{0} && bytes.back() == std::byte{0}, "assembled push constants are misplaced");
|
||||
shaders[1].pushConstantOffset = 4;
|
||||
expectFailure([&] { AgcDriver::Graphics::AssemblePushConstants(shaders); }, "overlap");
|
||||
shaders[1].pushConstantOffset = 120;
|
||||
expectFailure([&] { AgcDriver::Graphics::AssemblePushConstants(shaders); }, "crosses a push constant slot");
|
||||
shaders[1].pushConstantOffset = AgcDriver::Graphics::PipelinePushConstantBytes - 4;
|
||||
expectFailure([&] { AgcDriver::Graphics::AssemblePushConstants(shaders); }, "outside the pipeline push constant block");
|
||||
shaders[1].pushConstantOffset = AgcDriver::Graphics::PipelinePushSlotBytes;
|
||||
const auto slotted = AgcDriver::Graphics::AssemblePushConstants(shaders);
|
||||
Require(slotted[128] == std::byte{2} && slotted[139] == std::byte{2} && slotted[8] == std::byte{0}, "a pixel shader in its push slot was misplaced");
|
||||
shaders[1].pushConstantOffset = 2;
|
||||
expectFailure([&] { AgcDriver::Graphics::AssemblePushConstants(shaders); }, "DWORD aligned");
|
||||
shaders[1].pushConstantOffset = 8;
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ public:
|
|||
VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT libraryProperties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_PROPERTIES_EXT};
|
||||
VkPhysicalDeviceProperties2 properties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, &libraryProperties};
|
||||
function<PFN_vkGetPhysicalDeviceProperties2>("vkGetPhysicalDeviceProperties2")(context.physical, &properties);
|
||||
context.graphicsPipelineLibrary = library.graphicsPipelineLibrary == VK_TRUE && dynamicState.extendedDynamicState == VK_TRUE && rendering.dynamicRendering == VK_TRUE && libraryProperties.graphicsPipelineLibraryFastLinking == VK_TRUE;
|
||||
context.graphicsPipelineLibrary = library.graphicsPipelineLibrary == VK_TRUE && dynamicState.extendedDynamicState == VK_TRUE && rendering.dynamicRendering == VK_TRUE && libraryProperties.graphicsPipelineLibraryFastLinking == VK_TRUE && properties.properties.limits.maxPushConstantsSize >= PipelinePushConstantBytes;
|
||||
}
|
||||
rendering = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR, address.pNext, VK_TRUE};
|
||||
library = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT, &dynamicState, VK_TRUE};
|
||||
|
|
@ -3815,12 +3815,21 @@ void pipelineLibraryTests(const Device& device) {
|
|||
input.attributes.push_back({0, 0, VK_FORMAT_R32G32B32A32_SFLOAT, 0});
|
||||
static_cast<void>(lookup());
|
||||
Require(counters().built == std::array<std::uint64_t, 4>{2, 1, 1, 2} && counters().linked == 4, "a vertex input change rebuilt more than the vertex input library");
|
||||
Require(StagePushOffset(88, ShaderRecompiler::ShaderStage::Fragment, true) == PipelinePushSlotBytes && StagePushOffset(88, ShaderRecompiler::ShaderStage::Fragment, false) == 88 && StagePushOffset(40, ShaderRecompiler::ShaderStage::TessellationEvaluation, true) == 40, "a stage got the wrong push offset");
|
||||
const auto beforeSlots = counters().built;
|
||||
ShaderRecompiler::RecompileResult otherVertex = vertex;
|
||||
otherVertex.variantId = 14;
|
||||
std::array slotted{CompiledShader{ShaderRecompiler::ShaderStage::Vertex, &vertex, 0}, CompiledShader{ShaderRecompiler::ShaderStage::Fragment, &fragment, PipelinePushSlotBytes}};
|
||||
static_cast<void>(CachedPipeline(context, state, input, resources, slotted, VK_IMAGE_LAYOUT_GENERAL));
|
||||
slotted.front().program = &otherVertex;
|
||||
static_cast<void>(CachedPipeline(context, state, input, resources, slotted, VK_IMAGE_LAYOUT_GENERAL));
|
||||
Require(counters().built[2] == beforeSlots[2] + 1u && counters().built[1] == beforeSlots[1] + 1u, "a pixel shader in its fixed slot built its library again for another vertex shader");
|
||||
fragment.variantId = 13;
|
||||
static_cast<void>(lookup());
|
||||
Require(counters().built == std::array<std::uint64_t, 4>{2, 1, 2, 2}, "a new pixel shader rebuilt more than the fragment shader library");
|
||||
Require(counters().built == std::array<std::uint64_t, 4>{2, 2, 3, 2}, "a new pixel shader rebuilt more than the fragment shader library");
|
||||
state.colors.front().format = VK_FORMAT_R16G16B16A16_SFLOAT;
|
||||
static_cast<void>(lookup());
|
||||
Require(counters().built == std::array<std::uint64_t, 4>{2, 1, 2, 3}, "another target format rebuilt more than the fragment output library");
|
||||
Require(counters().built == std::array<std::uint64_t, 4>{2, 2, 3, 3}, "another target format rebuilt more than the fragment output library");
|
||||
ClearCachedPipelines(context.device);
|
||||
Require(counters().linked == 0, "clearing the pipelines kept the device's libraries");
|
||||
std::cout << "Pipeline library reuse tests passed\n";
|
||||
|
|
|
|||
|
|
@ -86,13 +86,13 @@ void Draw(AgcDriver::VulkanDevice& device, VkConservativeRasterizationModeEXT mo
|
|||
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(PixelCode.data()), PixelCode, 0, {}},
|
||||
{WaveSize, 0, pixelUserData, std::nullopt, pixel, std::nullopt, pixelMemory},
|
||||
target,
|
||||
{0, 0, vertexPush, 128 - vertexPush}
|
||||
PixelPushLayout(vertexPush, target)
|
||||
};
|
||||
fragment.useCache = false;
|
||||
const auto pixelResult = ShaderRecompiler::Recompile(fragment);
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
|
||||
{ShaderStage::Vertex, &vertexResult, 0},
|
||||
{ShaderStage::Fragment, &pixelResult, vertexPush}
|
||||
{ShaderStage::Fragment, &pixelResult, PixelPushOffset(vertexPush, target)}
|
||||
}};
|
||||
|
||||
AgcDriver::Graphics::State state{};
|
||||
|
|
|
|||
|
|
@ -78,13 +78,13 @@ void Draw(AgcDriver::VulkanDevice& device, float frontBias, float backBias) {
|
|||
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(PixelCode.data()), PixelCode, 0, {}},
|
||||
{WaveSize, 0, pixelUserData, std::nullopt, pixel, std::nullopt, pixelMemory},
|
||||
target,
|
||||
{0, 0, vertexPush, 128 - vertexPush}
|
||||
PixelPushLayout(vertexPush, target)
|
||||
};
|
||||
fragment.useCache = false;
|
||||
const auto pixelResult = ShaderRecompiler::Recompile(fragment);
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
|
||||
{ShaderStage::Vertex, &vertexResult, 0},
|
||||
{ShaderStage::Fragment, &pixelResult, vertexPush}
|
||||
{ShaderStage::Fragment, &pixelResult, PixelPushOffset(vertexPush, target)}
|
||||
}};
|
||||
|
||||
AgcDriver::Graphics::State state{};
|
||||
|
|
|
|||
|
|
@ -285,10 +285,10 @@ public:
|
|||
pixelInfo.wave32 = true;
|
||||
pixelInfo.targetOutputMode[0] = 9u;
|
||||
pixelInfo.targetExportMapping.fill(0xe4u);
|
||||
ShaderRecompiler::RecompileRequest pixelRequest{{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(PixelCode.data()), PixelCode, 0, {}}, {32u, 0u, {}, std::nullopt, pixelInfo, std::nullopt, {}}, device->Target(), {0u, 0u, vertexPush, 128u - vertexPush}};
|
||||
ShaderRecompiler::RecompileRequest pixelRequest{{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(PixelCode.data()), PixelCode, 0, {}}, {32u, 0u, {}, std::nullopt, pixelInfo, std::nullopt, {}}, device->Target(), PixelPushLayout(vertexPush, device->Target())};
|
||||
pixelRequest.useCache = false;
|
||||
const auto pixel = ShaderRecompiler::Recompile(pixelRequest);
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{{ShaderStage::Vertex, &vertex, 0u}, {ShaderStage::Fragment, &pixel, vertexPush}}};
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{{ShaderStage::Vertex, &vertex, 0u}, {ShaderStage::Fragment, &pixel, PixelPushOffset(vertexPush, device->Target())}}};
|
||||
Output.fill(Unwritten);
|
||||
device->Draw(State(), draw, shaders);
|
||||
device->WaitIdle();
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ void Draw(AgcDriver::VulkanDevice& device, const AgcDriver::Graphics::ColorTarge
|
|||
{ShaderStage::Fragment, codeAddress, pixelCode, 0, {}},
|
||||
{waveSize, 0, {}, std::nullopt, AgcDriver::Graphics::DecodePixelStageInfo(context, {}), std::nullopt, pixelMemory},
|
||||
target,
|
||||
{0, 0, vertexPush, 128 - vertexPush}
|
||||
PixelPushLayout(vertexPush, target)
|
||||
};
|
||||
const auto handle = ShaderRecompiler::PrepareShader(fragment);
|
||||
AgcDriver::DriverDetail::ShaderSnapshot snapshot{codeAddress, 0, 1, {pixelCode.begin(), pixelCode.end()}, {}};
|
||||
|
|
@ -257,7 +257,7 @@ void Draw(AgcDriver::VulkanDevice& device, const AgcDriver::Graphics::ColorTarge
|
|||
Require(plain->PipelineVariantId() != pixelResult->PipelineVariantId() && materialize(fragment)->PipelineVariantId() == pixelResult->PipelineVariantId(), "dual-source blending did not key its own specialization");
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
|
||||
{ShaderStage::Vertex, &vertexResult, 0},
|
||||
{ShaderStage::Fragment, pixelResult.get(), vertexPush}
|
||||
{ShaderStage::Fragment, pixelResult.get(), PixelPushOffset(vertexPush, target)}
|
||||
}};
|
||||
const AgcDriver::Pm4::DrawParameters draw{0, static_cast<std::uint32_t>(Triangle.size()), 0, 1, 0, false};
|
||||
device.Draw(state, draw, shaders);
|
||||
|
|
|
|||
|
|
@ -99,13 +99,13 @@ void Draw(AgcDriver::VulkanDevice& device) {
|
|||
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(PixelCode.data()), PixelCode, 0, {}},
|
||||
{64, 0, pixelUserData, std::nullopt, pixel, std::nullopt, pixelMemory},
|
||||
device.Target(),
|
||||
{0, 0, vertexPush, 128 - vertexPush}
|
||||
PixelPushLayout(vertexPush, device.Target())
|
||||
};
|
||||
fragment.useCache = false;
|
||||
const auto pixelResult = ShaderRecompiler::Recompile(fragment);
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
|
||||
{ShaderStage::Vertex, &vertexResult, 0},
|
||||
{ShaderStage::Fragment, &pixelResult, vertexPush}
|
||||
{ShaderStage::Fragment, &pixelResult, PixelPushOffset(vertexPush, device.Target())}
|
||||
}};
|
||||
|
||||
AgcDriver::Graphics::State state{};
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ ShaderRecompiler::RecompileResult RecompilePixel(std::span<const std::uint32_t>
|
|||
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(code.data()), code, 0, {}},
|
||||
{waveSize, 0, pixelUserData, std::nullopt, PixelStage(waveSize, input0), std::nullopt, pixelMemory},
|
||||
target,
|
||||
{0, 0, pushOffset, 128 - pushOffset}
|
||||
PixelPushLayout(pushOffset, target)
|
||||
};
|
||||
fragment.useCache = false;
|
||||
fragment.context.floatMode = ShaderRecompiler::ShaderFloatMode{0xf0u, true, true, false};
|
||||
|
|
@ -162,7 +162,7 @@ void Draw(AgcDriver::VulkanDevice& device, std::uint32_t waveSize) {
|
|||
const auto pixelResult = RecompilePixel(PixelCode, waveSize, Fp16Input, target, vertexPush);
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
|
||||
{ShaderStage::Vertex, &vertexResult, 0},
|
||||
{ShaderStage::Fragment, &pixelResult, vertexPush}
|
||||
{ShaderStage::Fragment, &pixelResult, PixelPushOffset(vertexPush, target)}
|
||||
}};
|
||||
|
||||
AgcDriver::Graphics::State state{};
|
||||
|
|
|
|||
|
|
@ -68,13 +68,13 @@ void Draw(AgcDriver::VulkanDevice& device, std::uint32_t waveSize) {
|
|||
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(PixelCode.data()), PixelCode, 0, {}},
|
||||
{waveSize, 0, pixelUserData, std::nullopt, pixel, std::nullopt, pixelMemory},
|
||||
target,
|
||||
{0, 0, vertexPush, 128 - vertexPush}
|
||||
PixelPushLayout(vertexPush, target)
|
||||
};
|
||||
fragment.useCache = false;
|
||||
const auto pixelResult = ShaderRecompiler::Recompile(fragment);
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
|
||||
{ShaderStage::Vertex, &vertexResult, 0},
|
||||
{ShaderStage::Fragment, &pixelResult, vertexPush}
|
||||
{ShaderStage::Fragment, &pixelResult, PixelPushOffset(vertexPush, target)}
|
||||
}};
|
||||
|
||||
AgcDriver::Graphics::State state{};
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ void Draw(AgcDriver::VulkanDevice& device, const AgcDriver::Graphics::ColorTarge
|
|||
{ShaderStage::Fragment, codeAddress, pixelCode, 0, {}},
|
||||
{waveSize, 0, {}, std::nullopt, AgcDriver::Graphics::DecodePixelStageInfo(context, {}), std::nullopt, pixelMemory},
|
||||
target,
|
||||
{0, 0, vertexPush, 128 - vertexPush}
|
||||
PixelPushLayout(vertexPush, target)
|
||||
};
|
||||
const auto handle = ShaderRecompiler::PrepareShader(fragment);
|
||||
AgcDriver::DriverDetail::ShaderSnapshot snapshot{codeAddress, 0, 1, {pixelCode.begin(), pixelCode.end()}, {}};
|
||||
|
|
@ -238,7 +238,7 @@ void Draw(AgcDriver::VulkanDevice& device, const AgcDriver::Graphics::ColorTarge
|
|||
Require(plain->PipelineVariantId() != pixelResult->PipelineVariantId() && materialize(fragment)->PipelineVariantId() == pixelResult->PipelineVariantId(), "the 10_11_11 unorm packing did not key its own specialization");
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
|
||||
{ShaderStage::Vertex, &vertexResult, 0},
|
||||
{ShaderStage::Fragment, pixelResult.get(), vertexPush}
|
||||
{ShaderStage::Fragment, pixelResult.get(), PixelPushOffset(vertexPush, target)}
|
||||
}};
|
||||
const AgcDriver::Pm4::DrawParameters draw{0, static_cast<std::uint32_t>(Triangle.size()), 0, 1, 0, false};
|
||||
device.Draw(state, draw, shaders);
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ ShaderRecompiler::RecompileResult Draw(AgcDriver::VulkanDevice& device, std::spa
|
|||
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(pixelCode.data()), pixelCode, 0, {}},
|
||||
{64u, 0, pixelUserData, std::nullopt, pixel, std::nullopt, pixelMemory},
|
||||
target,
|
||||
{0, 0, vertexPush, 128 - vertexPush}
|
||||
PixelPushLayout(vertexPush, target)
|
||||
};
|
||||
fragment.useCache = false;
|
||||
auto pixelResult = ShaderRecompiler::Recompile(fragment);
|
||||
|
|
@ -102,7 +102,7 @@ ShaderRecompiler::RecompileResult Draw(AgcDriver::VulkanDevice& device, std::spa
|
|||
geometry = ShaderRecompiler::BuildBarycentricGeometryShader(vertexResult, pixelResult, target, device.GeometryLimits());
|
||||
shaders.push_back({ShaderStage::Geometry, &geometry, 0});
|
||||
}
|
||||
shaders.push_back({ShaderStage::Fragment, &pixelResult, vertexPush});
|
||||
shaders.push_back({ShaderStage::Fragment, &pixelResult, PixelPushOffset(vertexPush, target)});
|
||||
|
||||
AgcDriver::Graphics::State state{};
|
||||
state.stages = {AgcDriver::Graphics::ShaderPath::Vertex, 0u, 64u, 64u, std::nullopt, std::nullopt};
|
||||
|
|
|
|||
|
|
@ -176,13 +176,13 @@ void Draw(AgcDriver::VulkanDevice& device, const AgcDriver::Graphics::ColorTarge
|
|||
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(pixelCode.data()), pixelCode, 0, {}},
|
||||
{waveSize, 0, pixelUserData, std::nullopt, pixel, std::nullopt, pixelMemory},
|
||||
target,
|
||||
{0, 0, vertexPush, 128 - vertexPush}
|
||||
PixelPushLayout(vertexPush, target)
|
||||
};
|
||||
fragment.useCache = false;
|
||||
const auto pixelResult = ShaderRecompiler::Recompile(fragment);
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
|
||||
{ShaderStage::Vertex, &vertexResult, 0},
|
||||
{ShaderStage::Fragment, &pixelResult, vertexPush}
|
||||
{ShaderStage::Fragment, &pixelResult, PixelPushOffset(vertexPush, target)}
|
||||
}};
|
||||
|
||||
VkPipelineColorBlendAttachmentState blend{};
|
||||
|
|
|
|||
|
|
@ -87,13 +87,13 @@ void Draw(AgcDriver::VulkanDevice& device, std::uint32_t waveSize, const ShaderR
|
|||
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(PixelCode.data()), PixelCode, 0, {}},
|
||||
{waveSize, 0, pixelUserData, std::nullopt, pixel, std::nullopt, pixelMemory},
|
||||
target,
|
||||
{0, 0, vertexPush, 128 - vertexPush}
|
||||
PixelPushLayout(vertexPush, target)
|
||||
};
|
||||
fragment.useCache = false;
|
||||
const auto pixelResult = ShaderRecompiler::Recompile(fragment);
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
|
||||
{ShaderStage::Vertex, &vertexResult, 0},
|
||||
{ShaderStage::Fragment, &pixelResult, vertexPush}
|
||||
{ShaderStage::Fragment, &pixelResult, PixelPushOffset(vertexPush, target)}
|
||||
}};
|
||||
|
||||
AgcDriver::Graphics::State state{};
|
||||
|
|
|
|||
|
|
@ -137,13 +137,13 @@ void Draw(AgcDriver::VulkanDevice& device, std::uint32_t waveSize, std::span<con
|
|||
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(pixelCode.data()), pixelCode, 0, {}},
|
||||
{waveSize, 0, pixelUserData, std::nullopt, pixel, std::nullopt, pixelMemory},
|
||||
target,
|
||||
{0, 0, vertexPush, 128 - vertexPush}
|
||||
PixelPushLayout(vertexPush, target)
|
||||
};
|
||||
fragment.useCache = false;
|
||||
const auto pixelResult = ShaderRecompiler::Recompile(fragment);
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
|
||||
{ShaderStage::Vertex, &vertexResult, 0},
|
||||
{ShaderStage::Fragment, &pixelResult, vertexPush}
|
||||
{ShaderStage::Fragment, &pixelResult, PixelPushOffset(vertexPush, target)}
|
||||
}};
|
||||
|
||||
AgcDriver::Graphics::State state{};
|
||||
|
|
|
|||
|
|
@ -102,13 +102,13 @@ void Draw(AgcDriver::VulkanDevice& device, std::uint32_t waveSize, bool bounded)
|
|||
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(pixelCode.data()), pixelCode, 0, {}},
|
||||
{waveSize, 0, pixelUserData, std::nullopt, pixel, std::nullopt, pixelMemory},
|
||||
target,
|
||||
{0, 0, vertexPush, 128 - vertexPush}
|
||||
PixelPushLayout(vertexPush, target)
|
||||
};
|
||||
fragment.useCache = false;
|
||||
const auto pixelResult = ShaderRecompiler::Recompile(fragment);
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
|
||||
{ShaderStage::Vertex, &vertexResult, 0},
|
||||
{ShaderStage::Fragment, &pixelResult, vertexPush}
|
||||
{ShaderStage::Fragment, &pixelResult, PixelPushOffset(vertexPush, target)}
|
||||
}};
|
||||
|
||||
AgcDriver::Graphics::State state{};
|
||||
|
|
|
|||
|
|
@ -95,13 +95,13 @@ void Draw(AgcDriver::VulkanDevice& device, std::uint32_t waveSize) {
|
|||
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(pixelCode.data()), pixelCode, 0, {}},
|
||||
{waveSize, 0, pixelUserData, std::nullopt, pixel, std::nullopt, pixelMemory},
|
||||
target,
|
||||
{0, 0, vertexPush, 128 - vertexPush}
|
||||
PixelPushLayout(vertexPush, target)
|
||||
};
|
||||
fragment.useCache = false;
|
||||
const auto pixelResult = ShaderRecompiler::Recompile(fragment);
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
|
||||
{ShaderStage::Vertex, &vertexResult, 0},
|
||||
{ShaderStage::Fragment, &pixelResult, vertexPush}
|
||||
{ShaderStage::Fragment, &pixelResult, PixelPushOffset(vertexPush, target)}
|
||||
}};
|
||||
|
||||
AgcDriver::Graphics::State state{};
|
||||
|
|
|
|||
|
|
@ -187,13 +187,13 @@ void Draw(AgcDriver::VulkanDevice& device, const AgcDriver::Graphics::ColorTarge
|
|||
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(pixelCode.data()), pixelCode, 0, {}},
|
||||
{waveSize, 0, pixelUserData, std::nullopt, pixel, std::nullopt, pixelMemory},
|
||||
target,
|
||||
{0, 0, vertexPush, 128 - vertexPush}
|
||||
PixelPushLayout(vertexPush, target)
|
||||
};
|
||||
fragment.useCache = false;
|
||||
const auto pixelResult = ShaderRecompiler::Recompile(fragment);
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
|
||||
{ShaderStage::Vertex, &vertexResult, 0},
|
||||
{ShaderStage::Fragment, &pixelResult, vertexPush}
|
||||
{ShaderStage::Fragment, &pixelResult, PixelPushOffset(vertexPush, target)}
|
||||
}};
|
||||
|
||||
AgcDriver::Graphics::State state{};
|
||||
|
|
|
|||
|
|
@ -71,13 +71,13 @@ void Draw(AgcDriver::VulkanDevice& device, std::uint32_t waveSize, std::span<con
|
|||
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(pixelCode.data()), pixelCode, 0, {}},
|
||||
{waveSize, 0, pixelUserData, std::nullopt, pixel, std::nullopt, pixelMemory},
|
||||
target,
|
||||
{0, 0, vertexPush, 128 - vertexPush}
|
||||
PixelPushLayout(vertexPush, target)
|
||||
};
|
||||
fragment.useCache = false;
|
||||
const auto pixelResult = ShaderRecompiler::Recompile(fragment);
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
|
||||
{ShaderStage::Vertex, &vertexResult, 0},
|
||||
{ShaderStage::Fragment, &pixelResult, vertexPush}
|
||||
{ShaderStage::Fragment, &pixelResult, PixelPushOffset(vertexPush, target)}
|
||||
}};
|
||||
|
||||
AgcDriver::Graphics::State state{};
|
||||
|
|
|
|||
|
|
@ -105,13 +105,13 @@ void Draw(AgcDriver::VulkanDevice& device, std::span<const std::uint32_t> pixelC
|
|||
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(pixelCode.data()), pixelCode, 0, {}},
|
||||
{64u, 0, pixelUserData, std::nullopt, pixel, std::nullopt, pixelMemory},
|
||||
target,
|
||||
{0, 0, vertexPush, 128 - vertexPush}
|
||||
PixelPushLayout(vertexPush, target)
|
||||
};
|
||||
fragment.useCache = false;
|
||||
const auto pixelResult = ShaderRecompiler::Recompile(fragment);
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
|
||||
{ShaderStage::Vertex, &vertexResult, 0},
|
||||
{ShaderStage::Fragment, &pixelResult, vertexPush}
|
||||
{ShaderStage::Fragment, &pixelResult, PixelPushOffset(vertexPush, target)}
|
||||
}};
|
||||
|
||||
AgcDriver::Graphics::State state{};
|
||||
|
|
|
|||
|
|
@ -99,13 +99,13 @@ void Draw(AgcDriver::VulkanDevice& device, std::uint32_t select, bool onlyRestar
|
|||
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(PixelCode.data()), PixelCode, 0, {}},
|
||||
{64u, 0, pixelUserData, std::nullopt, pixel, std::nullopt, pixelMemory},
|
||||
device.Target(),
|
||||
{0, 0, vertexPush, 128 - vertexPush}
|
||||
PixelPushLayout(vertexPush, device.Target())
|
||||
};
|
||||
fragment.useCache = false;
|
||||
const auto pixelResult = ShaderRecompiler::Recompile(fragment);
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
|
||||
{ShaderStage::Vertex, &vertexResult, 0},
|
||||
{ShaderStage::Fragment, &pixelResult, vertexPush}
|
||||
{ShaderStage::Fragment, &pixelResult, PixelPushOffset(vertexPush, device.Target())}
|
||||
}};
|
||||
|
||||
AgcDriver::Graphics::State state{};
|
||||
|
|
|
|||
|
|
@ -227,13 +227,13 @@ void Draw(AgcDriver::VulkanDevice& device, std::vector<AgcDriver::Graphics::Colo
|
|||
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(pixelCode.data()), pixelCode, 0, {}},
|
||||
{waveSize, 0, pixelUserData, std::nullopt, pixel, std::nullopt, pixelMemory},
|
||||
target,
|
||||
{0, 0, vertexPush, 128 - vertexPush}
|
||||
PixelPushLayout(vertexPush, target)
|
||||
};
|
||||
fragment.useCache = false;
|
||||
const auto pixelResult = ShaderRecompiler::Recompile(fragment);
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
|
||||
{ShaderStage::Vertex, &vertexResult, 0},
|
||||
{ShaderStage::Fragment, &pixelResult, vertexPush}
|
||||
{ShaderStage::Fragment, &pixelResult, PixelPushOffset(vertexPush, target)}
|
||||
}};
|
||||
|
||||
AgcDriver::Graphics::State state{};
|
||||
|
|
|
|||
|
|
@ -179,13 +179,13 @@ void Draw(AgcDriver::VulkanDevice& device, const AgcDriver::Graphics::ColorTarge
|
|||
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(pixelCode.data()), pixelCode, 0, {}},
|
||||
{waveSize, 0, pixelUserData, std::nullopt, pixel, std::nullopt, pixelMemory},
|
||||
target,
|
||||
{0, 0, vertexPush, 128 - vertexPush}
|
||||
PixelPushLayout(vertexPush, target)
|
||||
};
|
||||
fragment.useCache = false;
|
||||
const auto pixelResult = ShaderRecompiler::Recompile(fragment);
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
|
||||
{ShaderStage::Vertex, &vertexResult, 0},
|
||||
{ShaderStage::Fragment, &pixelResult, vertexPush}
|
||||
{ShaderStage::Fragment, &pixelResult, PixelPushOffset(vertexPush, target)}
|
||||
}};
|
||||
|
||||
VkPipelineColorBlendAttachmentState blend{};
|
||||
|
|
|
|||
|
|
@ -27,4 +27,13 @@ inline std::unique_ptr<AgcDriver::VulkanDevice> OpenVulkanTestDevice() {
|
|||
}
|
||||
}
|
||||
|
||||
inline std::uint32_t PixelPushOffset(std::uint32_t vertexPush, const ShaderRecompiler::SpirvTarget& target) {
|
||||
return AgcDriver::Graphics::StagePushOffset(vertexPush, ShaderRecompiler::ShaderStage::Fragment, target.fixedPushSlots);
|
||||
}
|
||||
|
||||
inline ShaderRecompiler::BindingLayout PixelPushLayout(std::uint32_t vertexPush, const ShaderRecompiler::SpirvTarget& target) {
|
||||
const auto offset = PixelPushOffset(vertexPush, target);
|
||||
return {0, 0, offset, AgcDriver::Graphics::PipelinePushSlotBytes - offset % AgcDriver::Graphics::PipelinePushSlotBytes};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -88,13 +88,13 @@ void Draw(AgcDriver::VulkanDevice& device, const Case& value) {
|
|||
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(PixelCode.data()), PixelCode, 0, {}},
|
||||
{64u, 0, pixelUserData, std::nullopt, pixel, std::nullopt, pixelMemory},
|
||||
device.Target(),
|
||||
{0, 0, vertexPush, 128 - vertexPush}
|
||||
PixelPushLayout(vertexPush, device.Target())
|
||||
};
|
||||
fragment.useCache = false;
|
||||
const auto pixelResult = ShaderRecompiler::Recompile(fragment);
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
|
||||
{ShaderStage::Vertex, &vertexResult, 0},
|
||||
{ShaderStage::Fragment, &pixelResult, vertexPush}
|
||||
{ShaderStage::Fragment, &pixelResult, PixelPushOffset(vertexPush, device.Target())}
|
||||
}};
|
||||
|
||||
AgcDriver::Graphics::State state{};
|
||||
|
|
|
|||
|
|
@ -58,6 +58,9 @@ struct IrBindingLayout {
|
|||
[[nodiscard]] bool UsesPushData() const {
|
||||
return pushDataStartDword != PushData::NoStart;
|
||||
}
|
||||
[[nodiscard]] std::uint32_t PushSlotDword() const {
|
||||
return UsesPushData() ? pushDataStartDword / PushData::DwordCount * PushData::DwordCount : 0u;
|
||||
}
|
||||
void AdvancePushData(std::uint32_t& cursor) const {
|
||||
if (UsesPushData()) {
|
||||
cursor = pushDataStartDword + ShaderDataDwords();
|
||||
|
|
|
|||
|
|
@ -49,7 +49,8 @@ struct UniformFillPlan {
|
|||
std::array<IrValue*, 4> values{};
|
||||
};
|
||||
|
||||
inline constexpr std::uint32_t NativePushConstantSize = sizeof(PushData);
|
||||
inline constexpr std::uint32_t NativePushSlotSize = sizeof(PushData);
|
||||
inline constexpr std::uint32_t NativePushConstantSize = 2u * NativePushSlotSize;
|
||||
|
||||
struct IrResourcePlan {
|
||||
IrShaderStage stage = IrShaderStage::Unknown;
|
||||
|
|
|
|||
|
|
@ -89,6 +89,9 @@ BindingAllocationResult BindingAllocator::Allocate(IrProgram& program, const Bin
|
|||
if (layout.pushConstantOffsetBytes + layout.pushConstantSizeBytes > NativePushConstantSize) {
|
||||
fail("shader binding layout failed: push constant range exceeds the native push constant size");
|
||||
}
|
||||
if (layout.pushConstantSizeBytes != 0u && layout.pushConstantOffsetBytes / NativePushSlotSize != (layout.pushConstantOffsetBytes + layout.pushConstantSizeBytes - 1u) / NativePushSlotSize) {
|
||||
fail("shader binding layout failed: push constant range crosses a stage slot");
|
||||
}
|
||||
|
||||
const ShaderInfo& info = program.Resources().info;
|
||||
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@ struct SpirvTarget {
|
|||
bool nonConstantImageOffsets = false;
|
||||
std::uint32_t srgbDecodeFormats = 0;
|
||||
bool narrowSubgroupClock = false;
|
||||
bool fixedPushSlots = false;
|
||||
};
|
||||
|
||||
struct BindingLayout {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ std::uint32_t EmitShaderDataDwordLoad(SpirvEmitterState& state, std::uint32_t dw
|
|||
const auto value = state.module.AllocateId();
|
||||
if (layout.UsesPushData()) {
|
||||
const auto base = state.module.SpecializationConstant(TypeU32(state), PipelineSpecialization::PushDataOffset, layout.pushDataStartDword);
|
||||
const auto index = Binary(state, spv::OpIAdd, TypeU32(state), base, ConstantU32(state, dwordIndex));
|
||||
const auto index = Binary(state, spv::OpIAdd, TypeU32(state), Binary(state, spv::OpISub, TypeU32(state), base, ConstantU32(state, layout.PushSlotDword())), ConstantU32(state, dwordIndex));
|
||||
state.module.AddFunction(spv::OpAccessChain, TypePushConstantElementPointer(state), pointer, state.pushConstantVariable, ConstantU32(state, 0), index);
|
||||
} else if (state.shaderDataStorageVariable != 0) {
|
||||
state.module.AddFunction(spv::OpAccessChain, TypeStorageBufferElementPointer(state), pointer, state.shaderDataStorageVariable, ConstantU32(state, 0), ConstantU32(state, dwordIndex));
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ std::uint32_t PushConstantArrayType(SpirvEmitterState& state) {
|
|||
|
||||
std::uint32_t PushConstantBlockType(SpirvEmitterState& state) {
|
||||
return state.module.DecoratedType(spv::OpTypeStruct,
|
||||
{{spv::OpMemberDecorate, {0u, spv::DecorationOffset, 0u}},
|
||||
{{spv::OpMemberDecorate, {0u, spv::DecorationOffset, state.program.Metadata().bindings.PushSlotDword() * 4u}},
|
||||
{spv::OpDecorate, {spv::DecorationBlock}}},
|
||||
PushConstantArrayType(state));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ flowchart LR
|
|||
- A compute shader whose LDS plus its lock dword exceeds the host's `maxComputeSharedMemorySize` is not given a `Workgroup` array. `Recompile` sets `ShaderInfo::sharedMemoryBytes` for it and the SPIR-V backend addresses the LDS in a coherent storage buffer at descriptor set 1 (`WorkgroupMemoryDescriptorSet`), with a slice of `RecompileResult::workgroupMemoryDwords` per workgroup, indexed by `WorkgroupID` and `NumWorkGroups`. `VulkanDevice` keeps one such buffer, sized from the dispatch dimensions, and binds it after the resource set; programs that fit keep using shared memory.
|
||||
- `Driver::RegisterShader` splits shader preparation in two. `PlanRegistered` runs on the registering guest thread: it validates the registers and entry point, decodes the code and builds the recompile request, so invalid registrations still fail there. The RDNA to IR translation and resource plan (`PrepareShader`) run on a background pool of `hardware_concurrency() / 2` detached threads (at least 2, 64 MiB stacks).
|
||||
- The snapshot is published as pending. `SourceHandleFor`, `InvocationFor` and `ShaderPreparationTransaction::Edit` / `Read` wait for the pending job before reading `PreparedShaders`, and rethrow its failure. `APS5_SYNC_SHADER_PREPARE=1` prepares on the registering thread instead.
|
||||
- A draw's `Graphics::Pipeline` comes from `CachedPipeline`. When the device has `VK_EXT_graphics_pipeline_library` with fast linking, `VK_EXT_extended_dynamic_state` and `VK_KHR_dynamic_rendering`, draws render with `vkCmdBeginRenderingKHR` instead of render passes and the pipeline is linked from four libraries (vertex input, pre-rasterization shaders, fragment shader, fragment output) kept per device in [`PipelineLibrary.cpp`](../../core/libs/prx/libSceAgcDriver/Graphics/src/PipelineLibrary.cpp). A library built for one pipeline is reused by the next: another blend, vertex layout or target format builds only that part (the shader libraries name no attachment format), and cull mode, front face, depth and stencil state are dynamic, so changing them only links. Pipelines with per-face depth bias, rect lists, geometry shaders or a stage without a variant id are created whole, still with dynamic rendering; devices without the extensions (or with `APS5_NO_GPL=1`) keep render passes and whole pipelines.
|
||||
- A draw's `Graphics::Pipeline` comes from `CachedPipeline`. When the device has `VK_EXT_graphics_pipeline_library` with fast linking, `VK_EXT_extended_dynamic_state` and `VK_KHR_dynamic_rendering`, draws render with `vkCmdBeginRenderingKHR` instead of render passes and the pipeline is linked from four libraries (vertex input, pre-rasterization shaders, fragment shader, fragment output) kept per device in [`PipelineLibrary.cpp`](../../core/libs/prx/libSceAgcDriver/Graphics/src/PipelineLibrary.cpp). A library built for one pipeline is reused by the next: another blend, vertex layout or target format builds only that part (the shader libraries name no attachment format), and cull mode, front face, depth and stencil state are dynamic, so changing them only links. Push constants then use a 256-byte block with a 128-byte slot per side: the pre-rasterization stages stack from byte 0, the pixel shader always starts at byte 128 (`StagePushOffset`), so its library does not depend on how much push data its vertex shader uses. Pipelines with per-face depth bias, rect lists, geometry shaders or a stage without a variant id are created whole, still with dynamic rendering; devices without the extensions (or with `APS5_NO_GPL=1`) keep render passes and whole pipelines.
|
||||
|
||||
### Shader MODE register access
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue