Skip to content
Permalink
Browse files

Merge pull request #12116 from hrydgard/vulkan-pretransform

Vulkan: Implement pretransform (performance optimization)
  • Loading branch information...
unknownbrackets committed Jun 22, 2019
2 parents 12ab276 + b8bde71 commit 2f8f4c30ed775d5046996c99ac268062d173c62d
@@ -895,6 +895,20 @@ int clamp(int x, int a, int b) {
return x;
}

static std::string surface_transforms_to_string(VkSurfaceTransformFlagsKHR transformFlags) {
std::string str;
if (transformFlags & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) str += "IDENTITY ";
if (transformFlags & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR) str += "ROTATE_90 ";
if (transformFlags & VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR) str += "ROTATE_180 ";
if (transformFlags & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) str += "ROTATE_270 ";
if (transformFlags & VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR) str += "HMIRROR ";
if (transformFlags & VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR) str += "HMIRROR_90 ";
if (transformFlags & VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR) str += "HMIRROR_180 ";
if (transformFlags & VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR) str += "HMIRROR_270 ";
if (transformFlags & VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR) str += "INHERIT ";
return str;
}

bool VulkanContext::InitSwapchain() {
VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_devices_[physical_device_], surface_, &surfCapabilities_);
assert(res == VK_SUCCESS);
@@ -915,6 +929,7 @@ bool VulkanContext::InitSwapchain() {

if (physicalDeviceProperties_[physical_device_].properties.vendorID == VULKAN_VENDOR_IMGTEC) {
// Swap chain width hack to avoid issue #11743 (PowerVR driver bug).
// TODO: Check if still broken if pretransform is used!
swapChainExtent_.width &= ~31;
}

@@ -961,19 +976,52 @@ bool VulkanContext::InitSwapchain() {
// Application must settle for fewer images than desired:
desiredNumberOfSwapChainImages = surfCapabilities_.maxImageCount;
}


// We mostly follow the practices from
// https://arm-software.github.io/vulkan_best_practice_for_mobile_developers/samples/surface_rotation/surface_rotation_tutorial.html
//
VkSurfaceTransformFlagBitsKHR preTransform;
if (surfCapabilities_.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) {
std::string supportedTransforms = surface_transforms_to_string(surfCapabilities_.supportedTransforms);
std::string currentTransform = surface_transforms_to_string(surfCapabilities_.currentTransform);
ILOG("Supported transforms: %s", supportedTransforms.c_str());
ILOG("Current transform: %s", currentTransform.c_str());
g_display_rotation = DisplayRotation::ROTATE_0;
g_display_rot_matrix.setIdentity();
bool swapChainExtentSwap = false;
if (surfCapabilities_.currentTransform & (VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR | VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR)) {
preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
} else {
} else if (surfCapabilities_.currentTransform & (VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR)) {
// Normal, sensible rotations. Let's handle it.
preTransform = surfCapabilities_.currentTransform;
g_display_rot_matrix.setIdentity();
switch (surfCapabilities_.currentTransform) {
case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
g_display_rotation = DisplayRotation::ROTATE_90;
g_display_rot_matrix.setRotationZ90();
std::swap(swapChainExtent_.width, swapChainExtent_.height);
break;
case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
g_display_rotation = DisplayRotation::ROTATE_180;
g_display_rot_matrix.setRotationZ180();
break;
case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
g_display_rotation = DisplayRotation::ROTATE_270;
g_display_rot_matrix.setRotationZ270();
std::swap(swapChainExtent_.width, swapChainExtent_.height);
break;
}
} else {
// Let the OS rotate the image (potentially slow on many Android devices)
preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
}
std::string preTransformStr = surface_transforms_to_string(preTransform);
ILOG("Chosen pretransform transform: %s", preTransformStr.c_str());

VkSwapchainCreateInfoKHR swap_chain_info{ VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };
swap_chain_info.surface = surface_;
swap_chain_info.minImageCount = desiredNumberOfSwapChainImages;
swap_chain_info.imageFormat = swapchainFormat_;
swap_chain_info.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
swap_chain_info.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
swap_chain_info.imageExtent.width = swapChainExtent_.width;
swap_chain_info.imageExtent.height = swapChainExtent_.height;
swap_chain_info.preTransform = preTransform;
@@ -982,8 +1030,9 @@ bool VulkanContext::InitSwapchain() {
swap_chain_info.oldSwapchain = VK_NULL_HANDLE;
swap_chain_info.clipped = true;
swap_chain_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
if (surfCapabilities_.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT)
swap_chain_info.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
// Don't ask for TRANSFER_DST for the swapchain image, we don't use that.
// if (surfCapabilities_.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT)
// swap_chain_info.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;

#ifndef ANDROID
// We don't support screenshots on Android
@@ -1008,7 +1057,7 @@ bool VulkanContext::InitSwapchain() {
ELOG("vkCreateSwapchainKHR failed!");
return false;
}

ILOG("Created swapchain: %dx%d", swap_chain_info.imageExtent.width, swap_chain_info.imageExtent.height);
return true;
}

@@ -725,6 +725,7 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int
float u0 = 0.0f, u1 = 1.0f;
float v0 = 0.0f, v1 = 1.0f;

DrawTextureFlags flags = (vfb || g_Config.iBufFilter == SCALE_LINEAR) ? DRAWTEX_LINEAR : DRAWTEX_NEAREST;
if (useBufferedRendering_ && vfb && vfb->fbo) {
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP });
SetViewport2D(0, 0, vfb->renderWidth, vfb->renderHeight);
@@ -733,6 +734,7 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int
// We are drawing to the back buffer so need to flip.
if (needBackBufferYSwap_)
std::swap(v0, v1);
flags = flags | DRAWTEX_TO_BACKBUFFER;
float x, y, w, h;
CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, ROTATION_LOCKED_HORIZONTAL);
SetViewport2D(x, y, w, h);
@@ -741,7 +743,6 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int

MakePixelTexture(srcPixels, srcPixelFormat, srcStride, width, height, u1, v1);

DrawTextureFlags flags = (vfb || g_Config.iBufFilter == SCALE_LINEAR) ? DRAWTEX_LINEAR : DRAWTEX_NEAREST;
Bind2DShader();
DrawActiveTexture(dstX, dstY, width, height, vfb->bufferWidth, vfb->bufferHeight, u0, v0, u1, v1, ROTATION_LOCKED_HORIZONTAL, flags);
gpuStats.numUploads++;
@@ -808,6 +809,7 @@ void FramebufferManagerCommon::DrawFramebufferToOutput(const u8 *srcPixels, GEBu
std::swap(v0, v1);

DrawTextureFlags flags = g_Config.iBufFilter == SCALE_LINEAR ? DRAWTEX_LINEAR : DRAWTEX_NEAREST;
flags = flags | DRAWTEX_TO_BACKBUFFER;
if (cardboardSettings.enabled) {
// Left Eye Image
SetViewport2D(cardboardSettings.leftEyeXPosition, cardboardSettings.screenYPosition, cardboardSettings.screenWidth, cardboardSettings.screenHeight);
@@ -975,10 +977,11 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
draw_->BindFramebufferAsTexture(vfb->fbo, 0, Draw::FB_COLOR_BIT, 0);
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
Bind2DShader();
DrawTextureFlags flags = g_Config.iBufFilter == SCALE_LINEAR ? DRAWTEX_LINEAR : DRAWTEX_NEAREST;
flags = flags | DRAWTEX_TO_BACKBUFFER;
// We are doing the DrawActiveTexture call directly to the backbuffer here. Hence, we must
// flip V.
Bind2DShader();
if (needBackBufferYSwap_)
std::swap(v0, v1);
if (cardboardSettings.enabled) {
@@ -1007,6 +1010,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
CalculatePostShaderUniforms(vfb->bufferWidth, vfb->bufferHeight, renderWidth_, renderHeight_, &uniforms);
BindPostShader(uniforms);
DrawTextureFlags flags = g_Config.iBufFilter == SCALE_LINEAR ? DRAWTEX_LINEAR : DRAWTEX_NEAREST;
flags = flags | DRAWTEX_TO_BACKBUFFER;
DrawActiveTexture(0, 0, fbo_w, fbo_h, fbo_w, fbo_h, 0.0f, 0.0f, 1.0f, 1.0f, ROTATION_LOCKED_HORIZONTAL, flags);

draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
@@ -1026,6 +1030,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
std::swap(v0, v1);
Bind2DShader();
flags = (!postShaderIsUpscalingFilter_ && g_Config.iBufFilter == SCALE_LINEAR) ? DRAWTEX_LINEAR : DRAWTEX_NEAREST;
flags = flags | DRAWTEX_TO_BACKBUFFER;
if (g_Config.bEnableCardboard) {
// Left Eye Image
SetViewport2D(cardboardSettings.leftEyeXPosition, cardboardSettings.screenYPosition, cardboardSettings.screenWidth, cardboardSettings.screenHeight);
@@ -1049,6 +1054,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
if (needBackBufferYSwap_)
std::swap(v0, v1);
DrawTextureFlags flags = (!postShaderIsUpscalingFilter_ && g_Config.iBufFilter == SCALE_LINEAR) ? DRAWTEX_LINEAR : DRAWTEX_NEAREST;
flags = flags | DRAWTEX_TO_BACKBUFFER;

PostShaderUniforms uniforms{};
CalculatePostShaderUniforms(vfb->bufferWidth, vfb->bufferHeight, vfb->renderWidth, vfb->renderHeight, &uniforms);
@@ -155,8 +155,13 @@ enum DrawTextureFlags {
DRAWTEX_LINEAR = 1,
DRAWTEX_KEEP_TEX = 2,
DRAWTEX_KEEP_STENCIL_ALPHA = 4,
DRAWTEX_TO_BACKBUFFER = 8,
};

inline DrawTextureFlags operator | (const DrawTextureFlags &lhs, const DrawTextureFlags &rhs) {
return DrawTextureFlags((u32)lhs | (u32)rhs);
}

enum class TempFBO {
DEPAL,
BLIT,
@@ -24,6 +24,9 @@
// DbgNew is not compatible with Glslang
#ifdef DBG_NEW
#undef new
#undef free
#undef malloc
#undef realloc
#endif

#include "base/logging.h"
@@ -20,6 +20,7 @@

#include "profiler/profiler.h"

#include "base/display.h"
#include "base/timeutil.h"
#include "math/lin/matrix4x4.h"
#include "math/dataconv.h"
@@ -302,6 +303,16 @@ void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, floa
vtx[i].y = vtx[i].y * invDestH - 1.0f;
}

if ((flags & DRAWTEX_TO_BACKBUFFER) && g_display_rotation != DisplayRotation::ROTATE_0) {
for (int i = 0; i < 4; i++) {
// backwards notation, should fix that...
Vec3 v(vtx[i].x, vtx[i].y, 0.0f);
v = v * g_display_rot_matrix;
vtx[i].x = v.x;
vtx[i].y = v.y;
}
}

draw_->FlushState();

// TODO: Should probably use draw_ directly and not go low level
@@ -234,36 +234,6 @@ void Matrix4x4::setOrthoVulkan(float left, float right, float top, float bottom,
ww = 1.0f;
}

void Matrix4x4::setProjectionInf(const float near_plane, const float fov_horiz, const float aspect) {
empty();
float f = fov_horiz*0.5f;
xx = 1.0f / tanf(f);
yy = 1.0f / tanf(f*aspect);
zz = 1;
wz = -near_plane;
zw = 1.0f;
}

void Matrix4x4::setRotationAxisAngle(const Vec3 &axis, float angle) {
Quaternion quat;
quat.setRotation(axis, angle);
quat.toMatrix(this);
}

// from a (Position, Rotation, Scale) vec3 quat vec3 tuple
Matrix4x4 Matrix4x4::fromPRS(const Vec3 &positionv, const Quaternion &rotv, const Vec3 &scalev) {
Matrix4x4 newM;
newM.setIdentity();
Matrix4x4 rot, scale;
rotv.toMatrix(&rot);
scale.setScaling(scalev);
newM = rot * scale;
newM.wx = positionv.x;
newM.wy = positionv.y;
newM.wz = positionv.z;
return newM;
}

void Matrix4x4::toText(char *buffer, int len) const {
snprintf(buffer, len, "%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n",
xx,xy,xz,xw,

0 comments on commit 2f8f4c3

Please sign in to comment.
You can’t perform that action at this time.