fix(agc): support unrestricted vulkan depth range

This commit is contained in:
boykopovar 2026-09-16 07:24:00 +03:00
parent f0b13b6652
commit 9c78d10044
5 changed files with 21 additions and 5 deletions

View File

@ -57,6 +57,7 @@ struct VulkanDevice::State {
bool tessellationShader = false;
bool meshShader = false;
bool depthClipControl = false;
bool depthRangeUnrestricted = false;
VkPhysicalDeviceMeshShaderPropertiesEXT meshLimits{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_EXT};
template<typename TFunction>
@ -277,6 +278,8 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window) : state(std::make_u
meshFeatures.meshShader = state->meshShader;
std::vector<const char*> deviceExtensions;
if (window != nullptr) deviceExtensions.assign(presentationExtensions.begin(), presentationExtensions.end());
state->depthRangeUnrestricted = hasExtension(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME);
if (state->depthRangeUnrestricted) deviceExtensions.push_back(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME);
VkPhysicalDeviceDepthClipControlFeaturesEXT depthClipFeatures{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT};
if (hasExtension(VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME)) {
VkPhysicalDeviceFeatures2 features{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, &depthClipFeatures};
@ -538,7 +541,8 @@ void VulkanDevice::DrawIndexed(const Graphics::State& graphics, const Pm4::Index
state->tessellationShader,
state->meshShader,
state->meshLimits,
state->depthClipControl
state->depthClipControl,
state->depthRangeUnrestricted
};
Graphics::DrawIndexed(context, graphics, draw, shaders);
}

View File

@ -33,6 +33,7 @@ struct Context {
bool meshShader = false;
VkPhysicalDeviceMeshShaderPropertiesEXT meshLimits{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_EXT};
bool depthClipControl = false;
bool depthRangeUnrestricted = false;
template<typename TFunction>
TFunction Function(const char* name) const {

View File

@ -22,6 +22,8 @@ Pipeline::Pipeline(const Context& context, const State& state, const RenderTarge
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& viewport = state.viewport;
Require(std::isfinite(viewport.minDepth) && std::isfinite(viewport.maxDepth), "non-finite viewport depth range");
Require(context.depthRangeUnrestricted || (viewport.minDepth >= 0 && viewport.minDepth <= 1 && viewport.maxDepth >= 0 && viewport.maxDepth <= 1), "viewport depth outside [0, 1] requires VK_EXT_depth_range_unrestricted");
Require(std::isfinite(viewport.x) && std::isfinite(viewport.y) && std::isfinite(viewport.width) && std::isfinite(viewport.height), "viewport arithmetic overflow");
Require(viewport.width <= context.limits.maxViewportDimensions[0] && std::abs(viewport.height) <= context.limits.maxViewportDimensions[1], "viewport dimensions exceed device limits");
Require(viewport.x >= context.limits.viewportBoundsRange[0] && viewport.x + viewport.width <= context.limits.viewportBoundsRange[1], "viewport X exceeds device bounds");

View File

@ -218,12 +218,12 @@ State DecodeState(const QueueState& queue) {
const auto zo = readFloat(cx, 0x114);
const auto minDepth = result.negativeOneToOne ? zo - zs : zo;
const auto maxDepth = zo + zs;
if (!(xs > 0 && ys != 0 && minDepth >= 0 && minDepth <= 1 && maxDepth >= 0 && maxDepth <= 1)) {
if (!(xs > 0 && ys != 0 && std::isfinite(minDepth) && std::isfinite(maxDepth))) {
std::ostringstream message;
message << "AGC graphics: unsupported viewport transform: scale=(" << xs << ", " << ys << ", " << zs << "), offset=(" << xo << ", " << yo << ", " << zo << "), depth=(" << minDepth << ", " << maxDepth << "), negativeOneToOne=" << result.negativeOneToOne;
throw std::runtime_error(message.str());
}
Require(readFloat(cx, 0xb4) == std::min(minDepth, maxDepth) && readFloat(cx, 0xb5) == std::max(minDepth, maxDepth), "viewport depth clamp differs from transform");
Require(readFloat(cx, 0xb4) <= readFloat(cx, 0xb5), "inverted viewport depth clamp bounds");
result.viewport = {xo - xs, yo - ys, 2 * xs, 2 * ys, minDepth, maxDepth};
result.scissor = {{0, 0}, result.renderExtent};
intersect(result.scissor, cx, 0xc, true);

View File

@ -192,10 +192,19 @@ void DepthClipTests() {
const auto reversed = AgcDriver::Graphics::DecodeState(queue);
Require(reversed.viewport.minDepth == 1 && reversed.viewport.maxDepth == 0, "reversed depth transform is incorrect");
queue.context[0x113] = std::bit_cast<std::uint32_t>(1.0f);
queue.context[0x114] = 0;
const auto unrestricted = AgcDriver::Graphics::DecodeState(queue);
Require(unrestricted.viewport.minDepth == -1 && unrestricted.viewport.maxDepth == 1, "unrestricted viewport depth was normalized");
queue.context[0x113] = std::bit_cast<std::uint32_t>(-1.0f);
const auto unrestrictedReversed = AgcDriver::Graphics::DecodeState(queue);
Require(unrestrictedReversed.viewport.minDepth == 1 && unrestrictedReversed.viewport.maxDepth == -1, "reversed unrestricted viewport depth changed");
queue.context[0x113] = 0x7f7fffff;
queue.context[0x114] = 0x7f7fffff;
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "unsupported viewport transform");
queue.context[0x114] = std::bit_cast<std::uint32_t>(0.5f);
queue.context[0x113] = std::bit_cast<std::uint32_t>(0.5f);
queue.context[0xb4] = std::bit_cast<std::uint32_t>(0.25f);
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "viewport depth clamp differs");
queue.context[0xb4] = std::bit_cast<std::uint32_t>(2.0f);
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "inverted viewport depth clamp");
queue.context[0xb4] = 0;
for (std::uint32_t bit = 0; bit < 32; ++bit) {
if (bit == 19) continue;