mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-10 05:08:31 +09:00
[Fix] (MG_Backend/DirectVulkan): fix prerotated default framebuffer
This commit is contained in:
@@ -184,6 +184,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
swapchainCaps.minImageExtent.height,
|
swapchainCaps.minImageExtent.height,
|
||||||
swapchainCaps.maxImageExtent.height);
|
swapchainCaps.maxImageExtent.height);
|
||||||
}
|
}
|
||||||
|
const VkExtent2D defaultFramebufferExtent = createInfo.imageExtent;
|
||||||
if (swapchainCaps.currentTransform == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR ||
|
if (swapchainCaps.currentTransform == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR ||
|
||||||
swapchainCaps.currentTransform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) {
|
swapchainCaps.currentTransform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) {
|
||||||
std::swap(createInfo.imageExtent.width, createInfo.imageExtent.height);
|
std::swap(createInfo.imageExtent.width, createInfo.imageExtent.height);
|
||||||
@@ -254,10 +255,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
// Properly initialize Default FBO here
|
// Properly initialize Default FBO here
|
||||||
auto& defaultFBOInfo = MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo;
|
auto& defaultFBOInfo = MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo;
|
||||||
const Int extentWidth = static_cast<Int>(createInfo.imageExtent.width);
|
const Int extentWidth = static_cast<Int>(defaultFramebufferExtent.width);
|
||||||
const Int extentHeight = static_cast<Int>(createInfo.imageExtent.height);
|
const Int extentHeight = static_cast<Int>(defaultFramebufferExtent.height);
|
||||||
const SizeT defaultAttachmentByteSize =
|
const SizeT defaultAttachmentByteSize =
|
||||||
static_cast<SizeT>(createInfo.imageExtent.width) * static_cast<SizeT>(createInfo.imageExtent.height) * 4;
|
static_cast<SizeT>(defaultFramebufferExtent.width) *
|
||||||
|
static_cast<SizeT>(defaultFramebufferExtent.height) * 4;
|
||||||
|
|
||||||
auto* colorTex = static_cast<MG_State::GLState::TextureObject2D*>(defaultFBOInfo->colorAttachment.get());
|
auto* colorTex = static_cast<MG_State::GLState::TextureObject2D*>(defaultFBOInfo->colorAttachment.get());
|
||||||
colorTex->AllocateStorage(
|
colorTex->AllocateStorage(
|
||||||
|
|||||||
@@ -90,17 +90,53 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
return requestedAlpha;
|
return requestedAlpha;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ApplyGLViewportState(VkCommandBuffer commandBuffer, const IntVec2& fallbackExtent) {
|
static Bool IsQuarterTurnPreTransform(VkSurfaceTransformFlagBitsKHR preTransform) {
|
||||||
|
return preTransform == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR ||
|
||||||
|
preTransform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static IntVec2 ResolveDefaultFramebufferLogicalExtent(VkSurfaceTransformFlagBitsKHR preTransform,
|
||||||
|
const IntVec2& rawExtent) {
|
||||||
|
if (IsQuarterTurnPreTransform(preTransform)) {
|
||||||
|
return {rawExtent.y(), rawExtent.x()};
|
||||||
|
}
|
||||||
|
return rawExtent;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Int ScaleFramebufferCoordinate(Int value, Int fromExtent, Int toExtent) {
|
||||||
|
if (fromExtent <= 0 || toExtent <= 0) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return static_cast<Int>((static_cast<Int64>(value) * toExtent + fromExtent / 2) / fromExtent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ApplyGLViewportState(VkCommandBuffer commandBuffer,
|
||||||
|
const IntVec2& framebufferExtent,
|
||||||
|
VkSurfaceTransformFlagBitsKHR preTransform,
|
||||||
|
Bool isDefaultFramebuffer) {
|
||||||
const IntVec4& viewportState = MG_State::pGLContext->GetViewport();
|
const IntVec4& viewportState = MG_State::pGLContext->GetViewport();
|
||||||
const FloatVec2& depthRange = MG_State::pGLContext->GetDepthRange();
|
const FloatVec2& depthRange = MG_State::pGLContext->GetDepthRange();
|
||||||
|
const IntVec2 logicalExtent = isDefaultFramebuffer
|
||||||
|
? ResolveDefaultFramebufferLogicalExtent(preTransform, framebufferExtent)
|
||||||
|
: framebufferExtent;
|
||||||
|
|
||||||
|
Int viewportX = viewportState.x();
|
||||||
|
Int viewportY = viewportState.y();
|
||||||
|
Int viewportWidth = viewportState.z() > 0 ? viewportState.z() : logicalExtent.x();
|
||||||
|
Int viewportHeight = viewportState.w() > 0 ? viewportState.w() : logicalExtent.y();
|
||||||
|
|
||||||
|
if (isDefaultFramebuffer && IsQuarterTurnPreTransform(preTransform)) {
|
||||||
|
viewportX = ScaleFramebufferCoordinate(viewportX, logicalExtent.x(), framebufferExtent.x());
|
||||||
|
viewportY = ScaleFramebufferCoordinate(viewportY, logicalExtent.y(), framebufferExtent.y());
|
||||||
|
viewportWidth = ScaleFramebufferCoordinate(viewportWidth, logicalExtent.x(), framebufferExtent.x());
|
||||||
|
viewportHeight = ScaleFramebufferCoordinate(viewportHeight, logicalExtent.y(), framebufferExtent.y());
|
||||||
|
}
|
||||||
|
|
||||||
VkViewport viewport{};
|
VkViewport viewport{};
|
||||||
viewport.x = static_cast<float>(viewportState.x());
|
viewport.x = static_cast<float>(viewportX);
|
||||||
viewport.y = static_cast<float>(viewportState.y());
|
viewport.y = static_cast<float>(viewportY);
|
||||||
viewport.width =
|
viewport.width = static_cast<float>(viewportWidth);
|
||||||
static_cast<float>(viewportState.z() > 0 ? viewportState.z() : fallbackExtent.x());
|
viewport.height = static_cast<float>(viewportHeight);
|
||||||
viewport.height =
|
|
||||||
static_cast<float>(viewportState.w() > 0 ? viewportState.w() : fallbackExtent.y());
|
|
||||||
viewport.minDepth = depthRange.x();
|
viewport.minDepth = depthRange.x();
|
||||||
viewport.maxDepth = depthRange.y();
|
viewport.maxDepth = depthRange.y();
|
||||||
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
|
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
|
||||||
@@ -163,6 +199,33 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
return scissor;
|
return scissor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static VkRect2D MakeDefaultFramebufferScissorRect(const IntVec4& scissorBox,
|
||||||
|
const IntVec2& framebufferExtent,
|
||||||
|
VkSurfaceTransformFlagBitsKHR preTransform) {
|
||||||
|
if (!IsQuarterTurnPreTransform(preTransform)) {
|
||||||
|
return MakeClampedScissorRect(scissorBox, framebufferExtent);
|
||||||
|
}
|
||||||
|
|
||||||
|
const IntVec2 logicalExtent = ResolveDefaultFramebufferLogicalExtent(preTransform, framebufferExtent);
|
||||||
|
const Int logicalX0 = std::max<Int>(0, scissorBox.x());
|
||||||
|
const Int logicalY0 = std::max<Int>(0, scissorBox.y());
|
||||||
|
const Int logicalX1 = std::min<Int>(logicalExtent.x(), scissorBox.x() + std::max<Int>(0, scissorBox.z()));
|
||||||
|
const Int logicalY1 = std::min<Int>(logicalExtent.y(), scissorBox.y() + std::max<Int>(0, scissorBox.w()));
|
||||||
|
|
||||||
|
const Int rawX0 = ScaleFramebufferCoordinate(logicalX0, logicalExtent.x(), framebufferExtent.x());
|
||||||
|
const Int rawY0 = ScaleFramebufferCoordinate(logicalY0, logicalExtent.y(), framebufferExtent.y());
|
||||||
|
const Int rawX1 = ScaleFramebufferCoordinate(logicalX1, logicalExtent.x(), framebufferExtent.x());
|
||||||
|
const Int rawY1 = ScaleFramebufferCoordinate(logicalY1, logicalExtent.y(), framebufferExtent.y());
|
||||||
|
|
||||||
|
VkRect2D scissor{};
|
||||||
|
scissor.offset = {std::max<Int>(0, rawX0), std::max<Int>(0, rawY0)};
|
||||||
|
scissor.extent = {
|
||||||
|
static_cast<Uint32>(std::max<Int>(0, rawX1 - rawX0)),
|
||||||
|
static_cast<Uint32>(std::max<Int>(0, rawY1 - rawY0)),
|
||||||
|
};
|
||||||
|
return scissor;
|
||||||
|
}
|
||||||
|
|
||||||
static void ApplyStencilState(VkCommandBuffer commandBuffer) {
|
static void ApplyStencilState(VkCommandBuffer commandBuffer) {
|
||||||
const StencilFaceState& frontStencil = MG_State::pGLContext->GetStencilState(StencilFace::Front);
|
const StencilFaceState& frontStencil = MG_State::pGLContext->GetStencilState(StencilFace::Front);
|
||||||
const StencilFaceState& backStencil = MG_State::pGLContext->GetStencilState(StencilFace::Back);
|
const StencilFaceState& backStencil = MG_State::pGLContext->GetStencilState(StencilFace::Back);
|
||||||
@@ -1410,12 +1473,76 @@ void main() {
|
|||||||
case VK_FORMAT_B8G8R8A8_UNORM:
|
case VK_FORMAT_B8G8R8A8_UNORM:
|
||||||
case VK_FORMAT_B8G8R8A8_SNORM:
|
case VK_FORMAT_B8G8R8A8_SNORM:
|
||||||
case VK_FORMAT_B8G8R8A8_SRGB:
|
case VK_FORMAT_B8G8R8A8_SRGB:
|
||||||
|
case VK_FORMAT_B8G8R8A8_USCALED:
|
||||||
|
case VK_FORMAT_B8G8R8A8_SSCALED:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static VkExtent2D GetPresentedDumpExtent(VkExtent2D rawExtent,
|
||||||
|
VkSurfaceTransformFlagBitsKHR preTransform) {
|
||||||
|
if (IsQuarterTurnPreTransform(preTransform)) {
|
||||||
|
return {rawExtent.height, rawExtent.width};
|
||||||
|
}
|
||||||
|
return rawExtent;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Uint8* GetPresentedDumpPixel(const Uint8* rawPixels,
|
||||||
|
VkExtent2D rawExtent,
|
||||||
|
VkSurfaceTransformFlagBitsKHR preTransform,
|
||||||
|
Uint32 presentedX,
|
||||||
|
Uint32 presentedY) {
|
||||||
|
Uint32 rawX = presentedX;
|
||||||
|
Uint32 rawY = presentedY;
|
||||||
|
switch (preTransform) {
|
||||||
|
case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
|
||||||
|
rawX = rawExtent.width - 1 - presentedY;
|
||||||
|
rawY = presentedX;
|
||||||
|
break;
|
||||||
|
case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
|
||||||
|
rawX = rawExtent.width - 1 - presentedX;
|
||||||
|
rawY = rawExtent.height - 1 - presentedY;
|
||||||
|
break;
|
||||||
|
case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
|
||||||
|
rawX = presentedY;
|
||||||
|
rawY = rawExtent.height - 1 - presentedX;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return rawPixels + (static_cast<SizeT>(rawY) * rawExtent.width + rawX) * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Bool WritePresentedDumpPpm(const char* path,
|
||||||
|
const Uint8* rawPixels,
|
||||||
|
VkExtent2D rawExtent,
|
||||||
|
VkFormat rawFormat,
|
||||||
|
VkSurfaceTransformFlagBitsKHR preTransform) {
|
||||||
|
FILE* dump = std::fopen(path, "wb");
|
||||||
|
if (dump == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VkExtent2D presentedExtent = GetPresentedDumpExtent(rawExtent, preTransform);
|
||||||
|
const Bool presentIsBgra = IsBgraVkFormat(rawFormat);
|
||||||
|
std::fprintf(dump, "P6\n%u %u\n255\n", presentedExtent.width, presentedExtent.height);
|
||||||
|
for (Uint32 y = 0; y < presentedExtent.height; ++y) {
|
||||||
|
for (Uint32 x = 0; x < presentedExtent.width; ++x) {
|
||||||
|
const Uint8* p = GetPresentedDumpPixel(rawPixels, rawExtent, preTransform, x, y);
|
||||||
|
const Uint8 rgb[3] = {
|
||||||
|
presentIsBgra ? p[2] : p[0],
|
||||||
|
p[1],
|
||||||
|
presentIsBgra ? p[0] : p[2],
|
||||||
|
};
|
||||||
|
std::fwrite(rgb, 1, sizeof(rgb), dump);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::fclose(dump);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static SizeT AlignPixelRow(SizeT rowBytes, Int alignment) {
|
static SizeT AlignPixelRow(SizeT rowBytes, Int alignment) {
|
||||||
const SizeT resolvedAlignment = static_cast<SizeT>(std::max(alignment, 1));
|
const SizeT resolvedAlignment = static_cast<SizeT>(std::max(alignment, 1));
|
||||||
return (rowBytes + resolvedAlignment - 1) & ~(resolvedAlignment - 1);
|
return (rowBytes + resolvedAlignment - 1) & ~(resolvedAlignment - 1);
|
||||||
@@ -3045,7 +3172,8 @@ void main() {
|
|||||||
MOBILEGL_ASSERT(idxUploadOk, "SetupDraw skipped: failed to upload index buffer");
|
MOBILEGL_ASSERT(idxUploadOk, "SetupDraw skipped: failed to upload index buffer");
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplyGLViewportState(frame.commandBuffer, renderPassEntry->extent);
|
ApplyGLViewportState(frame.commandBuffer, renderPassEntry->extent,
|
||||||
|
m_swapchainObject.GetPreTransform(), drawFbo->IsDefaultFramebuffer());
|
||||||
ApplyBlendConstants(frame.commandBuffer);
|
ApplyBlendConstants(frame.commandBuffer);
|
||||||
ApplyPolygonOffsetState(frame.commandBuffer);
|
ApplyPolygonOffsetState(frame.commandBuffer);
|
||||||
ApplyLineWidthState(frame.commandBuffer);
|
ApplyLineWidthState(frame.commandBuffer);
|
||||||
@@ -3055,7 +3183,10 @@ void main() {
|
|||||||
VkRect2D scissor{};
|
VkRect2D scissor{};
|
||||||
if (scissorEnabled) {
|
if (scissorEnabled) {
|
||||||
const auto& scissorBox = MG_State::pGLContext->GetScissorBox();
|
const auto& scissorBox = MG_State::pGLContext->GetScissorBox();
|
||||||
scissor = MakeClampedScissorRect(scissorBox, renderPassEntry->extent);
|
scissor = drawFbo->IsDefaultFramebuffer()
|
||||||
|
? MakeDefaultFramebufferScissorRect(scissorBox, renderPassEntry->extent,
|
||||||
|
m_swapchainObject.GetPreTransform())
|
||||||
|
: MakeClampedScissorRect(scissorBox, renderPassEntry->extent);
|
||||||
} else {
|
} else {
|
||||||
scissor.offset = {0, 0};
|
scissor.offset = {0, 0};
|
||||||
scissor.extent = { (Uint)renderPassEntry->extent.x(), (Uint)renderPassEntry->extent.y() };
|
scissor.extent = { (Uint)renderPassEntry->extent.x(), (Uint)renderPassEntry->extent.y() };
|
||||||
@@ -3523,7 +3654,8 @@ void main() {
|
|||||||
const Bool ok = VkRenderPassManager::BeginRenderPass(frame.commandBuffer, renderPassEntry);
|
const Bool ok = VkRenderPassManager::BeginRenderPass(frame.commandBuffer, renderPassEntry);
|
||||||
MOBILEGL_ASSERT(ok, "%s: BeginRenderPass failed", __func__);
|
MOBILEGL_ASSERT(ok, "%s: BeginRenderPass failed", __func__);
|
||||||
|
|
||||||
ApplyGLViewportState(frame.commandBuffer, renderPassEntry.extent);
|
ApplyGLViewportState(frame.commandBuffer, renderPassEntry.extent,
|
||||||
|
m_swapchainObject.GetPreTransform(), drawIsDefaultFbo);
|
||||||
|
|
||||||
VkRect2D scissor{};
|
VkRect2D scissor{};
|
||||||
scissor.offset = {0, 0};
|
scissor.offset = {0, 0};
|
||||||
@@ -5102,25 +5234,9 @@ void main() {
|
|||||||
presentStatsExtent.width, presentStatsExtent.height);
|
presentStatsExtent.width, presentStatsExtent.height);
|
||||||
}
|
}
|
||||||
if (shouldDumpPresent) {
|
if (shouldDumpPresent) {
|
||||||
FILE* dump = std::fopen(presentDumpPath, "wb");
|
const VkFormat presentFormat = m_swapchainObject.GetSurfaceFormat().format;
|
||||||
if (dump != nullptr) {
|
if (WritePresentedDumpPpm(presentDumpPath, pixels, presentStatsExtent, presentFormat,
|
||||||
const VkFormat presentFormat = m_swapchainObject.GetSurfaceFormat().format;
|
m_swapchainObject.GetPreTransform())) {
|
||||||
const Bool presentIsBgra = presentFormat == VK_FORMAT_B8G8R8A8_UNORM ||
|
|
||||||
presentFormat == VK_FORMAT_B8G8R8A8_SRGB ||
|
|
||||||
presentFormat == VK_FORMAT_B8G8R8A8_SNORM ||
|
|
||||||
presentFormat == VK_FORMAT_B8G8R8A8_USCALED ||
|
|
||||||
presentFormat == VK_FORMAT_B8G8R8A8_SSCALED;
|
|
||||||
std::fprintf(dump, "P6\n%u %u\n255\n", presentStatsExtent.width, presentStatsExtent.height);
|
|
||||||
for (SizeT i = 0; i < pixelCount; ++i) {
|
|
||||||
const Uint8* p = pixels + i * 4;
|
|
||||||
const Uint8 rgb[3] = {
|
|
||||||
presentIsBgra ? p[2] : p[0],
|
|
||||||
p[1],
|
|
||||||
presentIsBgra ? p[0] : p[2],
|
|
||||||
};
|
|
||||||
std::fwrite(rgb, 1, sizeof(rgb), dump);
|
|
||||||
}
|
|
||||||
std::fclose(dump);
|
|
||||||
if (PresentStatsEnabled()) {
|
if (PresentStatsEnabled()) {
|
||||||
std::fprintf(stderr, "MOBILEGL_PRESENT_DUMP_WRITTEN path=%s bytes=%zu\n",
|
std::fprintf(stderr, "MOBILEGL_PRESENT_DUMP_WRITTEN path=%s bytes=%zu\n",
|
||||||
presentDumpPath, static_cast<SizeT>(pixelCount * 3));
|
presentDumpPath, static_cast<SizeT>(pixelCount * 3));
|
||||||
|
|||||||
Reference in New Issue
Block a user