mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-27 15:30:35 +00:00
Vulkan: Apply the desired pretransform when drawing to the backbuffer.
This should save a lot of memory bandwidth on mobile devices that can't rotate images natively in the display engine. Fixes #12099.
This commit is contained in:
parent
9def42d800
commit
e943724905
@ -929,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;
|
||||
}
|
||||
|
||||
@ -975,17 +976,46 @@ 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;
|
||||
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());
|
||||
if (surfCapabilities_.supportedTransforms & (VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR | VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR)) {
|
||||
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_;
|
||||
|
@ -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 (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
|
||||
|
@ -111,6 +111,34 @@ public:
|
||||
zz = 1.0f;
|
||||
ww = 1.0f;
|
||||
}
|
||||
// Exact angles to avoid any artifacts.
|
||||
void setRotationZ90() {
|
||||
empty();
|
||||
float c = 0.0f;
|
||||
float s = 1.0f;
|
||||
xx = c; xy = s;
|
||||
yx = -s; yy = c;
|
||||
zz = 1.0f;
|
||||
ww = 1.0f;
|
||||
}
|
||||
void setRotationZ180() {
|
||||
empty();
|
||||
float c = -1.0f;
|
||||
float s = 0.0f;
|
||||
xx = c; xy = s;
|
||||
yx = -s; yy = c;
|
||||
zz = 1.0f;
|
||||
ww = 1.0f;
|
||||
}
|
||||
void setRotationZ270() {
|
||||
empty();
|
||||
float c = 0.0f;
|
||||
float s = -1.0f;
|
||||
xx = c; xy = s;
|
||||
yx = -s; yy = c;
|
||||
zz = 1.0f;
|
||||
ww = 1.0f;
|
||||
}
|
||||
void setRotationAxisAngle(const Vec3 &axis, float angle);
|
||||
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/display.h"
|
||||
#include "thin3d/thin3d.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/ColorConv.h"
|
||||
@ -352,6 +353,35 @@ DrawContext::~DrawContext() {
|
||||
DestroyPresets();
|
||||
}
|
||||
|
||||
void DrawContext::RotateRectToDisplay(FRect &rect, float curRTWidth, float curRTHeight) {
|
||||
if (g_display_rotation == DisplayRotation::ROTATE_0)
|
||||
return;
|
||||
switch (g_display_rotation) {
|
||||
case DisplayRotation::ROTATE_180:
|
||||
rect.x = curRTWidth - rect.w - rect.x;
|
||||
rect.y = curRTHeight - rect.h - rect.y;
|
||||
break;
|
||||
case DisplayRotation::ROTATE_90: {
|
||||
// Note that curRTWidth_ and curRTHeight_ are "swapped"!
|
||||
float origX = rect.x;
|
||||
float origY = rect.y;
|
||||
float rtw = curRTHeight;
|
||||
float rth = curRTWidth;
|
||||
rect.x = rth - rect.h - origY;
|
||||
rect.y = origX;
|
||||
std::swap(rect.w, rect.h);
|
||||
break;
|
||||
}
|
||||
case DisplayRotation::ROTATE_270: {
|
||||
float origX = rect.x;
|
||||
float origY = rect.y;
|
||||
// TODO
|
||||
std::swap(rect.w, rect.h);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: SSE/NEON
|
||||
// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :)
|
||||
void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format) {
|
||||
|
@ -661,6 +661,11 @@ public:
|
||||
virtual void FlushState() {}
|
||||
|
||||
protected:
|
||||
struct FRect {
|
||||
float x, y, w, h;
|
||||
};
|
||||
void RotateRectToDisplay(FRect &rect, float curRTWidth, float curRTHeight);
|
||||
|
||||
ShaderModule *vsPresets_[VS_MAX_PRESET];
|
||||
ShaderModule *fsPresets_[FS_MAX_PRESET];
|
||||
|
||||
|
@ -159,12 +159,7 @@ public:
|
||||
void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override;
|
||||
|
||||
private:
|
||||
struct FRect {
|
||||
float x, y, w, h;
|
||||
};
|
||||
|
||||
void ApplyCurrentState();
|
||||
void RotateRectToDisplay(FRect &rect);
|
||||
|
||||
HWND hWnd_;
|
||||
ID3D11Device *device_;
|
||||
@ -362,42 +357,12 @@ void D3D11DrawContext::HandleEvent(Event ev, int width, int height, void *param1
|
||||
}
|
||||
}
|
||||
|
||||
void D3D11DrawContext::RotateRectToDisplay(FRect &rect) {
|
||||
if (g_display_rotation == DisplayRotation::ROTATE_0)
|
||||
return;
|
||||
if (curRenderTargetView_ != bbRenderTargetView_)
|
||||
return; // Only the backbuffer is actually rotated wrong!
|
||||
switch (g_display_rotation) {
|
||||
case DisplayRotation::ROTATE_180:
|
||||
rect.x = curRTWidth_ - rect.w - rect.x;
|
||||
rect.y = curRTHeight_ - rect.h - rect.y;
|
||||
break;
|
||||
case DisplayRotation::ROTATE_90: {
|
||||
// Note that curRTWidth_ and curRTHeight_ are "swapped"!
|
||||
float origX = rect.x;
|
||||
float origY = rect.y;
|
||||
float rtw = curRTHeight_;
|
||||
float rth = curRTWidth_;
|
||||
rect.x = rth - rect.h - origY;
|
||||
rect.y = origX;
|
||||
std::swap(rect.w, rect.h);
|
||||
break;
|
||||
}
|
||||
case DisplayRotation::ROTATE_270: {
|
||||
float origX = rect.x;
|
||||
float origY = rect.y;
|
||||
// TODO
|
||||
std::swap(rect.w, rect.h);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void D3D11DrawContext::SetViewports(int count, Viewport *viewports) {
|
||||
D3D11_VIEWPORT vp[4];
|
||||
for (int i = 0; i < count; i++) {
|
||||
FRect rc{ viewports[i].TopLeftX , viewports[i].TopLeftY, viewports[i].Width, viewports[i].Height };
|
||||
RotateRectToDisplay(rc);
|
||||
if (curRenderTargetView_ == bbRenderTargetView_) // Only the backbuffer is actually rotated wrong!
|
||||
RotateRectToDisplay(rc, curRTWidth_, curRTHeight_);
|
||||
vp[i].TopLeftX = rc.x;
|
||||
vp[i].TopLeftY = rc.y;
|
||||
vp[i].Width = rc.w;
|
||||
@ -410,8 +375,8 @@ void D3D11DrawContext::SetViewports(int count, Viewport *viewports) {
|
||||
|
||||
void D3D11DrawContext::SetScissorRect(int left, int top, int width, int height) {
|
||||
FRect frc{ (float)left, (float)top, (float)width, (float)height };
|
||||
RotateRectToDisplay(frc);
|
||||
|
||||
if (curRenderTargetView_ == bbRenderTargetView_) // Only the backbuffer is actually rotated wrong!
|
||||
RotateRectToDisplay(frc, curRTWidth_, curRTHeight_);
|
||||
D3D11_RECT rc{};
|
||||
rc.left = (INT)frc.x;
|
||||
rc.top = (INT)frc.y;
|
||||
|
@ -1059,17 +1059,30 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
|
||||
}
|
||||
|
||||
void VKContext::SetScissorRect(int left, int top, int width, int height) {
|
||||
VkRect2D scissor{ {left, top}, {(uint32_t)width, (uint32_t)height} };
|
||||
FRect rc{ (float)left, (float)top, (float)width, (float)height };
|
||||
if (curFramebuffer_ == nullptr) { // Only the backbuffer is actually rotated wrong!
|
||||
int curRTWidth, curRTHeight;
|
||||
GetFramebufferDimensions((Framebuffer *)curFramebuffer_, &curRTWidth, &curRTHeight);
|
||||
RotateRectToDisplay(rc, (float)curRTWidth, (float)curRTHeight);
|
||||
}
|
||||
VkRect2D scissor{ {(int32_t)rc.x, (int32_t)rc.y}, {(uint32_t)rc.w, (uint32_t)rc.h} };
|
||||
renderManager_.SetScissor(scissor);
|
||||
}
|
||||
|
||||
void VKContext::SetViewports(int count, Viewport *viewports) {
|
||||
if (count > 0) {
|
||||
// Ignore viewports more than the first.
|
||||
VkViewport viewport;
|
||||
viewport.x = viewports[0].TopLeftX;
|
||||
viewport.y = viewports[0].TopLeftY;
|
||||
viewport.width = viewports[0].Width;
|
||||
viewport.height = viewports[0].Height;
|
||||
FRect rc{ viewports[0].TopLeftX , viewports[0].TopLeftY, viewports[0].Width, viewports[0].Height };
|
||||
if (curFramebuffer_ == nullptr) { // Only the backbuffer is actually rotated wrong!
|
||||
int curRTWidth, curRTHeight;
|
||||
GetFramebufferDimensions((Framebuffer *)curFramebuffer_, &curRTWidth, &curRTHeight);
|
||||
RotateRectToDisplay(rc, (float)curRTWidth, (float)curRTHeight);
|
||||
}
|
||||
viewport.x = rc.x;
|
||||
viewport.y = rc.y;
|
||||
viewport.width = rc.w;
|
||||
viewport.height = rc.h;
|
||||
viewport.minDepth = viewports[0].MinDepth;
|
||||
viewport.maxDepth = viewports[0].MaxDepth;
|
||||
renderManager_.SetViewport(viewport);
|
||||
|
Loading…
Reference in New Issue
Block a user