Merge branch 'master' into TemporaryHackishBranch

# Conflicts:
#	GPU/Directx9/PixelShaderGeneratorDX9.cpp
#	GPU/Directx9/VertexShaderGeneratorDX9.cpp
#	GPU/GLES/FragmentShaderGeneratorGLES.cpp
#	GPU/GLES/VertexShaderGeneratorGLES.cpp
#	GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp
#	Windows/GEDebugger/GEDebugger.h
This commit is contained in:
LunaMoo committed 2020-11-23 04:09:34 +01:00
commit 0779db5935
257 files changed
+7754 -7633

No files matched your search

+53
View File
@@ -0,0 +1,53 @@
# libretro CI
variables:
GIT_SUBMODULE_STRATEGY: normal
.core-defs:
variables:
CORENAME: ppsspp
CORE_ARGS: -DLIBRETRO=ON
.linux-defs:
variables:
EXTRA_PATH: lib
include:
- template: Jobs/Code-Quality.gitlab-ci.yml
- project: 'libretro-infrastructure/ci-templates'
file: '/android-cmake.yml'
- project: 'libretro-infrastructure/ci-templates'
file: '/linux-cmake.yml'
stages:
- build-prepare
- build-shared
- build-static
- test
#Desktop
libretro-build-linux-x86_64:
extends:
- .libretro-linux-cmake-x86_64
- .core-defs
- .linux-defs
# Android
libretro-build-android-armeabi-v7a:
extends:
- .libretro-android-cmake-armeabi-v7a
- .core-defs
libretro-build-android-arm64-v8a:
extends:
- .libretro-android-cmake-arm64-v8a
- .core-defs
libretro-build-android-x86_64:
extends:
- .libretro-android-cmake-x86_64
- .core-defs
libretro-build-android-x86:
extends:
- .libretro-android-cmake-x86
- .core-defs
+14 -18
View File
@@ -496,6 +496,12 @@ add_library(Common STATIC
Common/GPU/thin3d.cpp
Common/GPU/thin3d.h
Common/GPU/thin3d_create.h
Common/GPU/Shader.cpp
Common/GPU/Shader.h
Common/GPU/ShaderWriter.cpp
Common/GPU/ShaderWriter.h
Common/GPU/ShaderTranslation.h
Common/GPU/ShaderTranslation.cpp
Common/GPU/OpenGL/GLCommon.h
Common/GPU/OpenGL/GLDebugLog.cpp
Common/GPU/OpenGL/GLDebugLog.h
@@ -1235,8 +1241,6 @@ set(GPU_GLES
GPU/GLES/DepthBufferGLES.cpp
GPU/GLES/GPU_GLES.cpp
GPU/GLES/GPU_GLES.h
GPU/GLES/FragmentShaderGeneratorGLES.cpp
GPU/GLES/FragmentShaderGeneratorGLES.h
GPU/GLES/FragmentTestCacheGLES.cpp
GPU/GLES/FragmentTestCacheGLES.h
GPU/GLES/FramebufferManagerGLES.cpp
@@ -1252,8 +1256,6 @@ set(GPU_GLES
GPU/GLES/TextureScalerGLES.h
GPU/GLES/DrawEngineGLES.cpp
GPU/GLES/DrawEngineGLES.h
GPU/GLES/VertexShaderGeneratorGLES.cpp
GPU/GLES/VertexShaderGeneratorGLES.h
)
set(GPU_VULKAN
@@ -1263,8 +1265,6 @@ set(GPU_VULKAN
GPU/Vulkan/DebugVisVulkan.h
GPU/Vulkan/DrawEngineVulkan.cpp
GPU/Vulkan/DrawEngineVulkan.h
GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp
GPU/Vulkan/FragmentShaderGeneratorVulkan.h
GPU/Vulkan/FramebufferManagerVulkan.cpp
GPU/Vulkan/FramebufferManagerVulkan.h
GPU/Vulkan/GPU_Vulkan.cpp
@@ -1280,8 +1280,6 @@ set(GPU_VULKAN
GPU/Vulkan/TextureCacheVulkan.h
GPU/Vulkan/TextureScalerVulkan.cpp
GPU/Vulkan/TextureScalerVulkan.h
GPU/Vulkan/VertexShaderGeneratorVulkan.cpp
GPU/Vulkan/VertexShaderGeneratorVulkan.h
GPU/Vulkan/VulkanUtil.cpp
GPU/Vulkan/VulkanUtil.h
)
@@ -1291,8 +1289,6 @@ set(GPU_D3D9
GPU/Directx9/DepalettizeShaderDX9.h
GPU/Directx9/DrawEngineDX9.cpp
GPU/Directx9/DrawEngineDX9.h
GPU/Directx9/PixelShaderGeneratorDX9.cpp
GPU/Directx9/PixelShaderGeneratorDX9.h
GPU/Directx9/FramebufferManagerDX9.cpp
GPU/Directx9/FramebufferManagerDX9.h
GPU/Directx9/GPU_DX9.cpp
@@ -1306,8 +1302,8 @@ set(GPU_D3D9
GPU/Directx9/TextureCacheDX9.h
GPU/Directx9/TextureScalerDX9.cpp
GPU/Directx9/TextureScalerDX9.h
GPU/Directx9/VertexShaderGeneratorDX9.cpp
GPU/Directx9/VertexShaderGeneratorDX9.h
GPU/Directx9/VertexShaderGeneratorHLSL.cpp
GPU/GLES/VertexShaderGeneratorGLES.h
)
set(GPU_D3D11
@@ -1315,8 +1311,6 @@ set(GPU_D3D11
GPU/D3D11/DepalettizeShaderD3D11.h
GPU/D3D11/DrawEngineD3D11.cpp
GPU/D3D11/DrawEngineD3D11.h
GPU/D3D11/FragmentShaderGeneratorD3D11.cpp
GPU/D3D11/FragmentShaderGeneratorD3D11.h
GPU/D3D11/FramebufferManagerD3D11.cpp
GPU/D3D11/FramebufferManagerD3D11.h
GPU/D3D11/GPU_D3D11.cpp
@@ -1332,8 +1326,6 @@ set(GPU_D3D11
GPU/D3D11/TextureCacheD3D11.h
GPU/D3D11/TextureScalerD3D11.cpp
GPU/D3D11/TextureScalerD3D11.h
GPU/D3D11/VertexShaderGeneratorD3D11.cpp
GPU/D3D11/VertexShaderGeneratorD3D11.h
)
# We build Vulkan even on Apple to avoid annoying build differences.
@@ -1350,6 +1342,10 @@ set(GPU_SOURCES
${GPU_NEON}
GPU/Common/DepalettizeShaderCommon.cpp
GPU/Common/DepalettizeShaderCommon.h
GPU/Common/FragmentShaderGenerator.cpp
GPU/Common/FragmentShaderGenerator.h
GPU/Common/VertexShaderGenerator.cpp
GPU/Common/VertexShaderGenerator.h
GPU/Common/FramebufferManagerCommon.cpp
GPU/Common/FramebufferManagerCommon.h
GPU/Common/GPUDebugInterface.cpp
@@ -1360,14 +1356,14 @@ set(GPU_SOURCES
GPU/Common/DrawEngineCommon.h
GPU/Common/PresentationCommon.cpp
GPU/Common/PresentationCommon.h
GPU/Common/ReinterpretFramebuffer.cpp
GPU/Common/ReinterpretFramebuffer.h
GPU/Common/ShaderId.cpp
GPU/Common/ShaderId.h
GPU/Common/ShaderUniforms.cpp
GPU/Common/ShaderUniforms.h
GPU/Common/ShaderCommon.cpp
GPU/Common/ShaderCommon.h
GPU/Common/ShaderTranslation.cpp
GPU/Common/ShaderTranslation.h
GPU/Common/SplineCommon.cpp
GPU/Common/SplineCommon.h
GPU/Common/StencilCommon.cpp
+7
View File
@@ -386,6 +386,7 @@
<ClInclude Include="Data\Collections\ConstMap.h" />
<ClInclude Include="Data\Collections\FixedSizeQueue.h" />
<ClInclude Include="Data\Collections\Hashmaps.h" />
<ClInclude Include="Data\Collections\Slice.h" />
<ClInclude Include="Data\Collections\ThreadSafeList.h" />
<ClInclude Include="Data\Collections\TinySet.h" />
<ClInclude Include="Data\Color\RGBAUtil.h" />
@@ -428,6 +429,9 @@
<ClInclude Include="GPU\OpenGL\GLSLProgram.h" />
<ClInclude Include="GPU\OpenGL\GLCommon.h" />
<ClInclude Include="GPU\OpenGL\GLDebugLog.h" />
<ClInclude Include="GPU\Shader.h" />
<ClInclude Include="GPU\ShaderTranslation.h" />
<ClInclude Include="GPU\ShaderWriter.h" />
<ClInclude Include="GPU\thin3d.h" />
<ClInclude Include="GPU\thin3d_create.h" />
<ClInclude Include="GPU\Vulkan\VulkanContext.h" />
@@ -749,6 +753,9 @@
<ClCompile Include="GPU\OpenGL\GLSLProgram.cpp" />
<ClCompile Include="GPU\OpenGL\GLDebugLog.cpp" />
<ClCompile Include="GPU\OpenGL\thin3d_gl.cpp" />
<ClCompile Include="GPU\Shader.cpp" />
<ClCompile Include="GPU\ShaderTranslation.cpp" />
<ClCompile Include="GPU\ShaderWriter.cpp" />
<ClCompile Include="GPU\thin3d.cpp" />
<ClCompile Include="GPU\Vulkan\thin3d_vulkan.cpp" />
<ClCompile Include="GPU\Vulkan\VulkanContext.cpp" />
+21
View File
@@ -375,6 +375,18 @@
<ClInclude Include="Data\Collections\ThreadSafeList.h">
<Filter>Data\Collections</Filter>
</ClInclude>
<ClInclude Include="GPU\Shader.h">
<Filter>GPU</Filter>
</ClInclude>
<ClInclude Include="GPU\ShaderWriter.h">
<Filter>GPU</Filter>
</ClInclude>
<ClInclude Include="Data\Collections\Slice.h">
<Filter>Data\Collections</Filter>
</ClInclude>
<ClInclude Include="GPU\ShaderTranslation.h">
<Filter>GPU</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="ABI.cpp" />
@@ -723,6 +735,15 @@
<ClCompile Include="Render\Text\draw_text_win.cpp">
<Filter>Render\Text</Filter>
</ClCompile>
<ClCompile Include="GPU\ShaderWriter.cpp">
<Filter>GPU</Filter>
</ClCompile>
<ClCompile Include="GPU\Shader.cpp">
<Filter>GPU</Filter>
</ClCompile>
<ClCompile Include="GPU\ShaderTranslation.cpp">
<Filter>GPU</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="Crypto">
+48
View File
@@ -0,0 +1,48 @@
#pragma once
#include <vector>
// Like a const begin/end pair, just more convenient to use (and can only be used for linear array data).
// Inspired by Rust's slices and Google's StringPiece.
template <class T>
struct Slice {
// View some memory as a slice.
Slice(const T *data, size_t size) : data_(data), size_(size) {}
// Intentionally non-explicit.
// View a const array as a slice.
template<size_t N>
Slice(const T(&data)[N]) : data_(data), size_(N) {}
// Intentionally non-explicit.
// View a const array as a slice.
Slice(const std::vector<T> &data) : data_(data.data()), size_(data.size()) {}
const T &operator[](size_t index) const {
return data_[index];
}
size_t size() const {
return size_;
}
// "Iterators"
const T *begin() const {
return data_;
}
const T *end() const {
return data_ + size_;
}
static Slice empty() {
return Slice<T>(nullptr, 0);
}
bool is_empty() const {
return size_ == 0;
}
private:
const T *data_;
size_t size_;
};
+6
View File
@@ -450,6 +450,12 @@ void ConvertUTF8ToWString(wchar_t *dest, size_t destSize, const std::string &sou
MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, dest, std::min((int)destSize, size));
}
void ConvertUTF8ToWString(wchar_t *dest, size_t destSize, const char *source) {
int len = (int)strlen(source) + 1; // include trailing zero
int size = (int)MultiByteToWideChar(CP_UTF8, 0, source, len, NULL, 0);
MultiByteToWideChar(CP_UTF8, 0, source, len, dest, std::min((int)destSize, size));
}
std::wstring ConvertUTF8ToWString(const std::string &source) {
int len = (int)source.size();
int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, NULL, 0);
+1
View File
@@ -83,6 +83,7 @@ bool UTF8StringHasNonASCII(const char *utf8string);
std::string ConvertWStringToUTF8(const std::wstring &wstr);
std::string ConvertWStringToUTF8(const wchar_t *wstr);
void ConvertUTF8ToWString(wchar_t *dest, size_t destSize, const std::string &source);
void ConvertUTF8ToWString(wchar_t *dest, size_t destSize, const char *source);
std::wstring ConvertUTF8ToWString(const std::string &source);
#else
+86 -44
View File
@@ -233,6 +233,9 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de
hWnd_(hWnd),
deviceList_(deviceList) {
// We no longer support Windows Phone.
_assert_(featureLevel_ >= D3D_FEATURE_LEVEL_9_3);
// Seems like a fair approximation...
caps_.dualSourceBlend = featureLevel_ >= D3D_FEATURE_LEVEL_10_0;
caps_.depthClampSupported = featureLevel_ >= D3D_FEATURE_LEVEL_10_0;
@@ -291,6 +294,8 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de
packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
hr = device_->CreateTexture2D(&packDesc, nullptr, &packTexture_);
_assert_(SUCCEEDED(hr));
shaderLanguageDesc_.Init(HLSL_D3D11);
}
D3D11DrawContext::~D3D11DrawContext() {
@@ -659,6 +664,8 @@ InputLayout *D3D11DrawContext::CreateInputLayout(const InputLayoutDesc &desc) {
return inputLayout;
}
class D3D11ShaderModule;
class D3D11Pipeline : public Pipeline {
public:
~D3D11Pipeline() {
@@ -675,7 +682,7 @@ public:
if (dynamicUniforms)
dynamicUniforms->Release();
}
bool RequiresBuffer() {
bool RequiresBuffer() override {
return true;
}
@@ -689,6 +696,8 @@ public:
ID3D11GeometryShader *gs = nullptr;
D3D11_PRIMITIVE_TOPOLOGY topology = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;
std::vector<D3D11ShaderModule *> shaderModules;
size_t dynamicUniformsSize = 0;
ID3D11Buffer *dynamicUniforms = nullptr;
};
@@ -889,16 +898,14 @@ ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLang
std::string errors;
const char *target = nullptr;
switch (stage) {
case ShaderStage::FRAGMENT: target = fragmentModel; break;
case ShaderStage::VERTEX: target = vertexModel; break;
case ShaderStage::GEOMETRY:
case ShaderStage::Fragment: target = fragmentModel; break;
case ShaderStage::Vertex: target = vertexModel; break;
case ShaderStage::Geometry:
if (!geometryModel)
return nullptr;
target = geometryModel;
break;
case ShaderStage::COMPUTE:
case ShaderStage::CONTROL:
case ShaderStage::EVALUATION:
case ShaderStage::Compute:
default:
Crash();
break;
@@ -932,13 +939,13 @@ ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLang
module->stage = stage;
module->byteCode_ = std::vector<uint8_t>(data, data + dataSize);
switch (stage) {
case ShaderStage::VERTEX:
case ShaderStage::Vertex:
result = device_->CreateVertexShader(data, dataSize, nullptr, &module->vs);
break;
case ShaderStage::FRAGMENT:
case ShaderStage::Fragment:
result = device_->CreatePixelShader(data, dataSize, nullptr, &module->ps);
break;
case ShaderStage::GEOMETRY:
case ShaderStage::Geometry:
result = device_->CreateGeometryShader(data, dataSize, nullptr, &module->gs);
break;
default:
@@ -963,7 +970,9 @@ Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
dPipeline->raster = (D3D11RasterState *)desc.raster;
dPipeline->blend->AddRef();
dPipeline->depth->AddRef();
dPipeline->input->AddRef();
if (dPipeline->input) {
dPipeline->input->AddRef();
}
dPipeline->raster->AddRef();
dPipeline->topology = primToD3D11[(int)desc.prim];
if (desc.uniformDesc) {
@@ -980,21 +989,24 @@ Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
std::vector<D3D11ShaderModule *> shaders;
D3D11ShaderModule *vshader = nullptr;
for (auto iter : desc.shaders) {
iter->AddRef();
D3D11ShaderModule *module = (D3D11ShaderModule *)iter;
shaders.push_back(module);
switch (module->GetStage()) {
case ShaderStage::VERTEX:
case ShaderStage::Vertex:
vshader = module;
dPipeline->vs = module->vs;
break;
case ShaderStage::FRAGMENT:
case ShaderStage::Fragment:
dPipeline->ps = module->ps;
break;
case ShaderStage::GEOMETRY:
case ShaderStage::Geometry:
dPipeline->gs = module->gs;
break;
}
}
dPipeline->shaderModules = shaders;
if (!vshader) {
// No vertex shader - no graphics
@@ -1003,11 +1015,15 @@ Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
}
// Can finally create the input layout
auto &inputDesc = dPipeline->input->desc;
const std::vector<D3D11_INPUT_ELEMENT_DESC> &elements = dPipeline->input->elements;
HRESULT hr = device_->CreateInputLayout(elements.data(), (UINT)elements.size(), vshader->byteCode_.data(), vshader->byteCode_.size(), &dPipeline->il);
if (!SUCCEEDED(hr)) {
Crash();
if (dPipeline->input) {
auto &inputDesc = dPipeline->input->desc;
const std::vector<D3D11_INPUT_ELEMENT_DESC> &elements = dPipeline->input->elements;
HRESULT hr = device_->CreateInputLayout(elements.data(), (UINT)elements.size(), vshader->byteCode_.data(), vshader->byteCode_.size(), &dPipeline->il);
if (!SUCCEEDED(hr)) {
Crash();
}
} else {
dPipeline->il = nullptr;
}
return dPipeline;
}
@@ -1078,8 +1094,10 @@ void D3D11DrawContext::ApplyCurrentState() {
curTopology_ = curPipeline_->topology;
}
int numVBs = (int)curPipeline_->input->strides.size();
context_->IASetVertexBuffers(0, 1, nextVertexBuffers_, (UINT *)curPipeline_->input->strides.data(), (UINT *)nextVertexBufferOffsets_);
if (curPipeline_->input) {
int numVBs = (int)curPipeline_->input->strides.size();
context_->IASetVertexBuffers(0, numVBs, nextVertexBuffers_, (UINT *)curPipeline_->input->strides.data(), (UINT *)nextVertexBufferOffsets_);
}
if (dirtyIndexBuffer_) {
context_->IASetIndexBuffer(nextIndexBuffer_, DXGI_FORMAT_R16_UINT, nextIndexBufferOffset_);
dirtyIndexBuffer_ = false;
@@ -1204,7 +1222,10 @@ uint32_t D3D11DrawContext::GetDataFormatSupport(DataFormat fmt) const {
// A D3D11Framebuffer is a D3D11Framebuffer plus all the textures it owns.
class D3D11Framebuffer : public Framebuffer {
public:
D3D11Framebuffer() {}
D3D11Framebuffer(int width, int height) {
width_ = width;
height_ = height;
}
~D3D11Framebuffer() {
if (colorTex)
colorTex->Release();
@@ -1212,17 +1233,18 @@ public:
colorRTView->Release();
if (colorSRView)
colorSRView->Release();
if (depthSRView)
depthSRView->Release();
if (depthStencilTex)
depthStencilTex->Release();
if (depthStencilRTView)
depthStencilRTView->Release();
}
int width;
int height;
ID3D11Texture2D *colorTex = nullptr;
ID3D11RenderTargetView *colorRTView = nullptr;
ID3D11ShaderResourceView *colorSRView = nullptr;
ID3D11ShaderResourceView *depthSRView = nullptr;
DXGI_FORMAT colorFormat = DXGI_FORMAT_UNKNOWN;
ID3D11Texture2D *depthStencilTex = nullptr;
@@ -1232,9 +1254,7 @@ public:
Framebuffer *D3D11DrawContext::CreateFramebuffer(const FramebufferDesc &desc) {
HRESULT hr;
D3D11Framebuffer *fb = new D3D11Framebuffer();
fb->width = desc.width;
fb->height = desc.height;
D3D11Framebuffer *fb = new D3D11Framebuffer(desc.width, desc.height);
if (desc.numColorAttachments) {
fb->colorFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
D3D11_TEXTURE2D_DESC descColor{};
@@ -1273,11 +1293,11 @@ Framebuffer *D3D11DrawContext::CreateFramebuffer(const FramebufferDesc &desc) {
descDepth.Height = desc.height;
descDepth.MipLevels = 1;
descDepth.ArraySize = 1;
descDepth.Format = fb->depthStencilFormat;
descDepth.Format = DXGI_FORMAT_R24G8_TYPELESS; // so we can create an R24X8 view of it.
descDepth.SampleDesc.Count = 1;
descDepth.SampleDesc.Quality = 0;
descDepth.Usage = D3D11_USAGE_DEFAULT;
descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL;
descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
descDepth.CPUAccessFlags = 0;
descDepth.MiscFlags = 0;
hr = device_->CreateTexture2D(&descDepth, nullptr, &fb->depthStencilTex);
@@ -1286,7 +1306,7 @@ Framebuffer *D3D11DrawContext::CreateFramebuffer(const FramebufferDesc &desc) {
return nullptr;
}
D3D11_DEPTH_STENCIL_VIEW_DESC descDSV{};
descDSV.Format = descDepth.Format;
descDSV.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
descDSV.Texture2D.MipSlice = 0;
hr = device_->CreateDepthStencilView(fb->depthStencilTex, &descDSV, &fb->depthStencilRTView);
@@ -1294,6 +1314,17 @@ Framebuffer *D3D11DrawContext::CreateFramebuffer(const FramebufferDesc &desc) {
delete fb;
return nullptr;
}
D3D11_SHADER_RESOURCE_VIEW_DESC depthViewDesc{};
depthViewDesc.Format = DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
depthViewDesc.Texture2D.MostDetailedMip = 0;
depthViewDesc.Texture2D.MipLevels = 1;
depthViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
hr = device_->CreateShaderResourceView(fb->depthStencilTex, &depthViewDesc, &fb->depthSRView);
if (FAILED(hr)) {
WARN_LOG(G3D, "Failed to create SRV for depth buffer.");
fb->depthSRView = nullptr;
}
}
return fb;
@@ -1381,7 +1412,7 @@ void D3D11DrawContext::CopyFramebufferImage(Framebuffer *srcfb, int level, int x
}
// TODO: Check for level too!
if (width == src->width && width == dst->width && height == src->height && height == dst->height && x == 0 && y == 0 && z == 0 && dstX == 0 && dstY == 0 && dstZ == 0) {
if (width == src->Width() && width == dst->Width() && height == src->Height() && height == dst->Height() && x == 0 && y == 0 && z == 0 && dstX == 0 && dstY == 0 && dstZ == 0) {
// Don't need to specify region. This might be faster, too.
context_->CopyResource(dstTex, srcTex);
return;
@@ -1400,11 +1431,11 @@ void D3D11DrawContext::CopyFramebufferImage(Framebuffer *srcfb, int level, int x
dstY -= y;
y = 0;
}
if (x + width > src->width) {
width = src->width - x;
if (x + width > src->Width()) {
width = src->Width() - x;
}
if (y + height > src->height) {
height = src->height - y;
if (y + height > src->Height()) {
height = src->Height() - y;
}
D3D11_BOX srcBox{ (UINT)x, (UINT)y, (UINT)z, (UINT)(x + width), (UINT)(y + height), (UINT)(z + depth) };
context_->CopySubresourceRegion(dstTex, dstLevel, dstX, dstY, dstZ, srcTex, level, &srcBox);
@@ -1426,11 +1457,11 @@ bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channel
_assert_(fb->colorFormat == DXGI_FORMAT_R8G8B8A8_UNORM);
// TODO: Figure out where the badness really comes from.
if (bx + bw > fb->width) {
bw -= (bx + bw) - fb->width;
if (bx + bw > fb->Width()) {
bw -= (bx + bw) - fb->Width();
}
if (by + bh > fb->height) {
bh -= (by + bh) - fb->height;
if (by + bh > fb->Height()) {
bh -= (by + bh) - fb->Height();
}
}
@@ -1550,8 +1581,8 @@ void D3D11DrawContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const Ren
context_->OMSetRenderTargets(1, &fb->colorRTView, fb->depthStencilRTView);
curRenderTargetView_ = fb->colorRTView;
curDepthStencilView_ = fb->depthStencilRTView;
curRTWidth_ = fb->width;
curRTHeight_ = fb->height;
curRTWidth_ = fb->Width();
curRTHeight_ = fb->Height();
}
} else {
if (curRenderTargetView_ == bbRenderTargetView_ && curDepthStencilView_ == bbDepthStencilView_) {
@@ -1587,7 +1618,18 @@ void D3D11DrawContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const Ren
// color must be 0, for now.
void D3D11DrawContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) {
D3D11Framebuffer *fb = (D3D11Framebuffer *)fbo;
context_->PSSetShaderResources(binding, 1, &fb->colorSRView);
switch (channelBit) {
case FBChannel::FB_COLOR_BIT:
context_->PSSetShaderResources(binding, 1, &fb->colorSRView);
break;
case FBChannel::FB_DEPTH_BIT:
if (fb->depthSRView) {
context_->PSSetShaderResources(binding, 1, &fb->depthSRView);
}
break;
default:
break;
}
}
uintptr_t D3D11DrawContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) {
@@ -1608,8 +1650,8 @@ uintptr_t D3D11DrawContext::GetFramebufferAPITexture(Framebuffer *fbo, int chann
void D3D11DrawContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) {
D3D11Framebuffer *fb = (D3D11Framebuffer *)fbo;
if (fb) {
*w = fb->width;
*h = fb->height;
*w = fb->Width();
*h = fb->Height();
} else {
*w = bbWidth_;
*h = bbHeight_;
+41 -60
View File
@@ -1,18 +1,18 @@
#ifdef _WIN32
#include "ppsspp_config.h"
#ifdef _WIN32
#include "Common/CommonWindows.h"
#include "Common/GPU/D3D9/D3DCompilerLoader.h"
#include "Common/GPU/D3D9/D3D9ShaderCompiler.h"
#include "Common/CommonFuncs.h"
#include "Common/SysError.h"
#include "Common/Log.h"
#include "Common/StringUtils.h"
struct ID3DXConstantTable;
namespace DX9 {
bool CompilePixelShader(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT3DPIXELSHADER9 *pShader, ID3DXConstantTable **pShaderTable, std::string &errorMessage) {
LPD3DBLOB CompileShaderToByteCodeD3D9(const char *code, const char *target, std::string *errorMessage) {
LPD3DBLOB pShaderCode = nullptr;
LPD3DBLOB pErrorMsg = nullptr;
@@ -23,77 +23,58 @@ bool CompilePixelShader(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT3DPI
nullptr,
nullptr,
"main",
"ps_2_0",
target,
0,
0,
&pShaderCode,
&pErrorMsg);
if (pErrorMsg) {
errorMessage = (CHAR *)pErrorMsg->GetBufferPointer();
*errorMessage = std::string((CHAR *)pErrorMsg->GetBufferPointer());
OutputDebugStringUTF8(LineNumberString(std::string(code)).c_str());
OutputDebugStringUTF8(errorMessage->c_str());
pErrorMsg->Release();
} else if (FAILED(hr)) {
errorMessage = GetStringErrorMsg(hr);
} else {
errorMessage = "";
}
if (FAILED(hr) || !pShaderCode) {
if (pShaderCode)
if (pShaderCode) {
pShaderCode->Release();
return false;
pShaderCode = nullptr;
}
} else if (FAILED(hr)) {
*errorMessage = GetStringErrorMsg(hr);
if (pShaderCode) {
pShaderCode->Release();
pShaderCode = nullptr;
}
} else {
*errorMessage = "";
}
// Create pixel shader.
device->CreatePixelShader( (DWORD*)pShaderCode->GetBufferPointer(),
pShader );
pShaderCode->Release();
return true;
return pShaderCode;
}
bool CompileVertexShader(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT3DVERTEXSHADER9 *pShader, ID3DXConstantTable **pShaderTable, std::string &errorMessage) {
LPD3DBLOB pShaderCode = nullptr;
LPD3DBLOB pErrorMsg = nullptr;
// Compile pixel shader.
HRESULT hr = dyn_D3DCompile(code,
(UINT)strlen(code),
nullptr,
nullptr,
nullptr,
"main",
"vs_2_0",
0,
0,
&pShaderCode,
&pErrorMsg);
if (pErrorMsg) {
errorMessage = (CHAR *)pErrorMsg->GetBufferPointer();
pErrorMsg->Release();
} else if (FAILED(hr)) {
errorMessage = GetStringErrorMsg(hr);
bool CompilePixelShaderD3D9(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT3DPIXELSHADER9 *pShader, std::string *errorMessage) {
LPD3DBLOB pShaderCode = CompileShaderToByteCodeD3D9(code, "ps_2_0", errorMessage);
if (pShaderCode) {
// Create pixel shader.
device->CreatePixelShader((DWORD*)pShaderCode->GetBufferPointer(), pShader);
pShaderCode->Release();
return true;
} else {
errorMessage = "";
}
if (FAILED(hr) || !pShaderCode) {
if (pShaderCode)
pShaderCode->Release();
return false;
}
// Create pixel shader.
device->CreateVertexShader( (DWORD*)pShaderCode->GetBufferPointer(),
pShader );
pShaderCode->Release();
return true;
}
} // namespace
bool CompileVertexShaderD3D9(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT3DVERTEXSHADER9 *pShader, std::string *errorMessage) {
LPD3DBLOB pShaderCode = CompileShaderToByteCodeD3D9(code, "vs_2_0", errorMessage);
if (pShaderCode) {
// Create vertex shader.
device->CreateVertexShader((DWORD*)pShaderCode->GetBufferPointer(), pShader);
pShaderCode->Release();
return true;
} else {
return false;
}
}
#endif
+4 -5
View File
@@ -1,6 +1,7 @@
#pragma once
#include "Common/CommonWindows.h"
#include "Common/GPU/D3D9/D3DCompilerLoader.h"
#include <initguid.h>
#include <string>
@@ -8,9 +9,7 @@
struct ID3DXConstantTable;
namespace DX9 {
LPD3DBLOB CompileShaderToByteCodeD3D9(const char *code, const char *target, std::string *errorMessage);
bool CompilePixelShader(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT3DPIXELSHADER9 *pShader, ID3DXConstantTable **pShaderTable, std::string &errorMessage);
bool CompileVertexShader(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT3DVERTEXSHADER9 *pShader, ID3DXConstantTable **pShaderTable, std::string &errorMessage);
} // namespace DX9
bool CompilePixelShaderD3D9(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT3DPIXELSHADER9 *pShader, std::string *errorMessage);
bool CompileVertexShaderD3D9(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT3DVERTEXSHADER9 *pShader, std::string *errorMessage);
-9
View File
@@ -5,7 +5,6 @@
namespace DX9 {
DirectXState dxstate;
GLExtensions gl_extensions;
LPDIRECT3DDEVICE9 pD3Ddevice = nullptr;
LPDIRECT3DDEVICE9EX pD3DdeviceEx = nullptr;
@@ -64,14 +63,6 @@ void DirectXState::Restore() {
texAddressV.restore(); count++;
}
void CheckGLExtensions() {
static bool done = false;
if (done)
return;
done = true;
memset(&gl_extensions, 0, sizeof(gl_extensions));
}
} // namespace DX9
#endif // _MSC_VER
+23 -23
View File
@@ -254,7 +254,7 @@ public:
}
bool Compile(LPDIRECT3DDEVICE9 device, const uint8_t *data, size_t size);
void Apply(LPDIRECT3DDEVICE9 device) {
if (stage_ == ShaderStage::FRAGMENT) {
if (stage_ == ShaderStage::Fragment) {
device->SetPixelShader(pshader_);
} else {
device->SetVertexShader(vshader_);
@@ -670,6 +670,8 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID
HRESULT fboINTZ = d3d->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_A8R8G8B8, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, FOURCC_INTZ);
supportsINTZ = SUCCEEDED(displayINTZ) && SUCCEEDED(fboINTZ);
}
shaderLanguageDesc_.Init(HLSL_D3D9);
}
D3D9Context::~D3D9Context() {
@@ -697,10 +699,10 @@ Pipeline *D3D9Context::CreateGraphicsPipeline(const PipelineDesc &desc) {
delete pipeline;
return NULL;
}
if (iter->GetStage() == ShaderStage::FRAGMENT) {
if (iter->GetStage() == ShaderStage::Fragment) {
pipeline->pshader = static_cast<D3D9ShaderModule *>(iter);
}
else if (iter->GetStage() == ShaderStage::VERTEX) {
else if (iter->GetStage() == ShaderStage::Vertex) {
pipeline->vshader = static_cast<D3D9ShaderModule *>(iter);
}
}
@@ -1034,10 +1036,10 @@ bool D3D9ShaderModule::Compile(LPDIRECT3DDEVICE9 device, const uint8_t *data, si
auto compile = [&](const char *profile) -> HRESULT {
return dyn_D3DCompile(source, (UINT)strlen(source), nullptr, defines, includes, "main", profile, 0, 0, &codeBuffer, &errorBuffer);
};
HRESULT hr = compile(stage_ == ShaderStage::FRAGMENT ? "ps_2_0" : "vs_2_0");
HRESULT hr = compile(stage_ == ShaderStage::Fragment ? "ps_2_0" : "vs_2_0");
if (FAILED(hr) && hr == D3DXERR_INVALIDDATA) {
// Might be a post shader. Let's try using shader model 3.
hr = compile(stage_ == ShaderStage::FRAGMENT ? "ps_3_0" : "vs_3_0");
hr = compile(stage_ == ShaderStage::Fragment ? "ps_3_0" : "vs_3_0");
}
if (FAILED(hr)) {
const char *error = errorBuffer ? (const char *)errorBuffer->GetBufferPointer() : "(no errorbuffer returned)";
@@ -1055,7 +1057,7 @@ bool D3D9ShaderModule::Compile(LPDIRECT3DDEVICE9 device, const uint8_t *data, si
}
bool success = false;
if (stage_ == ShaderStage::FRAGMENT) {
if (stage_ == ShaderStage::Fragment) {
HRESULT result = device->CreatePixelShader((DWORD *)codeBuffer->GetBufferPointer(), &pshader_);
success = SUCCEEDED(result);
} else {
@@ -1072,28 +1074,26 @@ bool D3D9ShaderModule::Compile(LPDIRECT3DDEVICE9 device, const uint8_t *data, si
class D3D9Framebuffer : public Framebuffer {
public:
D3D9Framebuffer(int width, int height) {
width_ = width;
height_ = height;
}
~D3D9Framebuffer();
uint32_t id;
LPDIRECT3DSURFACE9 surf;
LPDIRECT3DSURFACE9 depthstencil;
LPDIRECT3DTEXTURE9 tex;
LPDIRECT3DTEXTURE9 depthstenciltex;
int width;
int height;
FBColorDepth colorDepth;
uint32_t id = 0;
LPDIRECT3DSURFACE9 surf = nullptr;
LPDIRECT3DSURFACE9 depthstencil = nullptr;
LPDIRECT3DTEXTURE9 tex = nullptr;
LPDIRECT3DTEXTURE9 depthstenciltex = nullptr;
};
Framebuffer *D3D9Context::CreateFramebuffer(const FramebufferDesc &desc) {
static uint32_t id = 0;
D3D9Framebuffer *fbo = new D3D9Framebuffer{};
fbo->width = desc.width;
fbo->height = desc.height;
fbo->colorDepth = desc.colorDepth;
D3D9Framebuffer *fbo = new D3D9Framebuffer(desc.width, desc.height);
fbo->depthstenciltex = nullptr;
HRESULT rtResult = device_->CreateTexture(fbo->width, fbo->height, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &fbo->tex, NULL);
HRESULT rtResult = device_->CreateTexture(desc.width, desc.height, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &fbo->tex, NULL);
if (FAILED(rtResult)) {
ERROR_LOG(G3D, "Failed to create render target");
delete fbo;
@@ -1103,12 +1103,12 @@ Framebuffer *D3D9Context::CreateFramebuffer(const FramebufferDesc &desc) {
HRESULT dsResult;
if (supportsINTZ) {
dsResult = device_->CreateTexture(fbo->width, fbo->height, 1, D3DUSAGE_DEPTHSTENCIL, FOURCC_INTZ, D3DPOOL_DEFAULT, &fbo->depthstenciltex, NULL);
dsResult = device_->CreateTexture(desc.width, desc.height, 1, D3DUSAGE_DEPTHSTENCIL, FOURCC_INTZ, D3DPOOL_DEFAULT, &fbo->depthstenciltex, NULL);
if (SUCCEEDED(dsResult)) {
dsResult = fbo->depthstenciltex->GetSurfaceLevel(0, &fbo->depthstencil);
}
} else {
dsResult = device_->CreateDepthStencilSurface(fbo->width, fbo->height, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, FALSE, &fbo->depthstencil, NULL);
dsResult = device_->CreateDepthStencilSurface(desc.width, desc.height, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, FALSE, &fbo->depthstencil, NULL);
}
if (FAILED(dsResult)) {
ERROR_LOG(G3D, "Failed to create depth buffer");
@@ -1210,8 +1210,8 @@ void D3D9Context::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChan
void D3D9Context::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) {
D3D9Framebuffer *fb = (D3D9Framebuffer *)fbo;
if (fb) {
*w = fb->width;
*h = fb->height;
*w = fb->Width();
*h = fb->Height();
} else {
*w = targetWidth_;
*h = targetHeight_;
+33 -26
View File
@@ -77,19 +77,28 @@ bool GLExtensions::VersionGEThan(int major, int minor, int sub) {
}
int GLExtensions::GLSLVersion() {
// Used for shader translation and core contexts (Apple drives fail without an exact match.)
if (gl_extensions.VersionGEThan(3, 3)) {
return gl_extensions.ver[0] * 100 + gl_extensions.ver[1] * 10;
} else if (gl_extensions.VersionGEThan(3, 2)) {
return 150;
} else if (gl_extensions.VersionGEThan(3, 1)) {
return 140;
} else if (gl_extensions.VersionGEThan(3, 0)) {
return 130;
} else if (gl_extensions.VersionGEThan(2, 1)) {
return 120;
if (gl_extensions.IsGLES) {
if (gl_extensions.GLES3) {
// GLSL version matches ES version.
return gl_extensions.ver[0] * 100 + gl_extensions.ver[1] * 10;
} else {
return 100;
}
} else {
return 110;
// Used for shader translation and core contexts (Apple drives fail without an exact match.)
if (gl_extensions.VersionGEThan(3, 3)) {
return gl_extensions.ver[0] * 100 + gl_extensions.ver[1] * 10;
} else if (gl_extensions.VersionGEThan(3, 2)) {
return 150;
} else if (gl_extensions.VersionGEThan(3, 1)) {
return 140;
} else if (gl_extensions.VersionGEThan(3, 0)) {
return 130;
} else if (gl_extensions.VersionGEThan(2, 1)) {
return 120;
} else {
return 110;
}
}
}
@@ -160,6 +169,7 @@ void CheckGLExtensions() {
gl_extensions.gpuVendor = GPU_VENDOR_IMGTEC;
} else if (vendor == "Qualcomm") {
gl_extensions.gpuVendor = GPU_VENDOR_QUALCOMM;
sscanf(renderer, "Adreno (TM) %d", &gl_extensions.modelNumber);
} else if (vendor == "Broadcom") {
gl_extensions.gpuVendor = GPU_VENDOR_BROADCOM;
// Just for reference: Galaxy Y has renderer == "VideoCore IV HW"
@@ -354,6 +364,9 @@ void CheckGLExtensions() {
gl_extensions.EXT_draw_instanced = g_set_gl_extensions.count("GL_EXT_draw_instanced") != 0;
gl_extensions.ARB_draw_instanced = g_set_gl_extensions.count("GL_ARB_draw_instanced") != 0;
gl_extensions.ARB_cull_distance = g_set_gl_extensions.count("GL_ARB_cull_distance") != 0;
gl_extensions.ARB_depth_clamp = g_set_gl_extensions.count("GL_ARB_depth_clamp") != 0;
gl_extensions.ARB_uniform_buffer_object = g_set_gl_extensions.count("GL_ARB_uniform_buffer_object") != 0;
gl_extensions.ARB_explicit_attrib_location = g_set_gl_extensions.count("GL_ARB_explicit_attrib_location") != 0;
if (gl_extensions.IsGLES) {
gl_extensions.OES_texture_npot = g_set_gl_extensions.count("GL_OES_texture_npot") != 0;
@@ -364,7 +377,6 @@ void CheckGLExtensions() {
gl_extensions.EXT_blend_minmax = g_set_gl_extensions.count("GL_EXT_blend_minmax") != 0;
gl_extensions.EXT_unpack_subimage = g_set_gl_extensions.count("GL_EXT_unpack_subimage") != 0;
gl_extensions.EXT_shader_framebuffer_fetch = g_set_gl_extensions.count("GL_EXT_shader_framebuffer_fetch") != 0;
gl_extensions.NV_shader_framebuffer_fetch = g_set_gl_extensions.count("GL_NV_shader_framebuffer_fetch") != 0;
gl_extensions.ARM_shader_framebuffer_fetch = g_set_gl_extensions.count("GL_ARM_shader_framebuffer_fetch") != 0;
gl_extensions.OES_texture_float = g_set_gl_extensions.count("GL_OES_texture_float") != 0;
gl_extensions.EXT_buffer_storage = g_set_gl_extensions.count("GL_EXT_buffer_storage") != 0;
@@ -471,9 +483,11 @@ void CheckGLExtensions() {
}
}
// Now, Adreno lies. So let's override it.
if (gl_extensions.gpuVendor == GPU_VENDOR_QUALCOMM) {
WARN_LOG(G3D, "Detected Adreno - lowering int precision");
// Now, old Adreno lies about supporting full precision integers. So let's override it.
// The model number comparison should probably be 400 or 500. This causes us to avoid depal-in-shader.
// It seems though that this caused large perf regressions on Adreno 5xx, so I've bumped it up to 600.
if (gl_extensions.gpuVendor == GPU_VENDOR_QUALCOMM && gl_extensions.modelNumber < 600) {
WARN_LOG(G3D, "Detected old Adreno - lowering reported int precision for safety");
gl_extensions.range[1][5][0] = 15;
gl_extensions.range[1][5][1] = 15;
}
@@ -498,14 +512,14 @@ void CheckGLExtensions() {
}
if (gl_extensions.VersionGEThan(3, 1)) {
gl_extensions.ARB_draw_instanced = true;
// ARB_uniform_buffer_object = true;
gl_extensions.ARB_uniform_buffer_object = true;
}
if (gl_extensions.VersionGEThan(3, 2)) {
// ARB_depth_clamp = true;
gl_extensions.ARB_depth_clamp = true;
}
if (gl_extensions.VersionGEThan(3, 3)) {
gl_extensions.ARB_blend_func_extended = true;
// ARB_explicit_attrib_location = true;
gl_extensions.ARB_explicit_attrib_location = true;
}
if (gl_extensions.VersionGEThan(4, 0)) {
// ARB_gpu_shader5 = true;
@@ -538,13 +552,6 @@ void CheckGLExtensions() {
}
}
#ifdef __APPLE__
if (!gl_extensions.IsGLES && !gl_extensions.IsCoreContext) {
// Apple doesn't allow OpenGL 3.x+ in compatibility contexts.
gl_extensions.ForceGL2 = true;
}
#endif
ProcessGPUFeatures();
int error = glGetError();
+4 -2
View File
@@ -34,11 +34,11 @@ struct GLExtensions {
int ver[3];
int gpuVendor;
char model[128];
int modelNumber;
bool IsGLES;
bool IsCoreContext;
bool GLES3; // true if the full OpenGL ES 3.0 is supported
bool ForceGL2;
// OES
bool OES_depth24;
@@ -55,6 +55,7 @@ struct GLExtensions {
bool ARB_pixel_buffer_object;
bool ARB_blend_func_extended; // dual source blending
bool EXT_blend_func_extended; // dual source blending (GLES, new 2015)
bool ARB_explicit_attrib_location;
bool ARB_shader_image_load_store;
bool ARB_shading_language_420pack;
bool ARB_conservative_depth;
@@ -64,6 +65,8 @@ struct GLExtensions {
bool ARB_draw_instanced;
bool ARB_buffer_storage;
bool ARB_cull_distance;
bool ARB_depth_clamp;
bool ARB_uniform_buffer_object;
// EXT
bool EXT_swap_control_tear;
@@ -82,7 +85,6 @@ struct GLExtensions {
bool EXT_clip_cull_distance;
// NV
bool NV_shader_framebuffer_fetch;
bool NV_copy_image;
bool NV_framebuffer_blit;
bool NV_pixel_buffer_object; // GL_NV_pixel_buffer_object
+54 -15
View File
@@ -277,21 +277,30 @@ void GLQueueRunner::RunInitSteps(const std::vector<GLRInitStep> &steps, bool ski
glCompileShader(shader);
GLint success = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
std::string infoLog = GetInfoLog(shader, glGetShaderiv, glGetShaderInfoLog);
if (!success) {
std::string infoLog = GetInfoLog(shader, glGetShaderiv, glGetShaderInfoLog);
#ifdef __ANDROID__
ERROR_LOG(G3D, "Error in shader compilation! %s\n", infoLog.c_str());
ERROR_LOG(G3D, "Shader source:\n%s\n", (const char *)code);
#endif
ERROR_LOG(G3D, "Error in shader compilation for: %s", step.create_shader.shader->desc.c_str());
ERROR_LOG(G3D, "Info log: %s", infoLog.c_str());
ERROR_LOG(G3D, "Shader source:\n%s\n", (const char *)code);
std::string errorString = StringFromFormat(
"Error in shader compilation for: %s\n"
"Info log: %s\n"
"Shader source:\n%s\n//END\n\n",
step.create_shader.shader->desc.c_str(),
infoLog.c_str(),
LineNumberString(code).c_str());
std::vector<std::string> lines;
SplitString(errorString, '\n', lines);
for (auto &line : lines) {
ERROR_LOG(G3D, "%s", line.c_str());
}
if (errorCallback_) {
std::string desc = StringFromFormat("Shader compilation failed: %s", step.create_shader.stage == GL_VERTEX_SHADER ? "vertex" : "fragment");
errorCallback_(desc.c_str(), errorString.c_str(), errorCallbackUserData_);
}
Reporting::ReportMessage("Error in shader compilation: info: %s\n%s\n%s", infoLog.c_str(), step.create_shader.shader->desc.c_str(), (const char *)code);
#ifdef SHADERLOG
OutputDebugStringUTF8(infoLog.c_str());
#endif
step.create_shader.shader->failed = true;
step.create_shader.shader->error = infoLog;
step.create_shader.shader->error = infoLog; // Hm, we never use this.
}
// Before we throw away the code, attach it to the shader for debugging.
step.create_shader.shader->code = code;
@@ -376,17 +385,21 @@ void GLQueueRunner::RunInitSteps(const std::vector<GLRInitStep> &steps, bool ski
CHECK_GL_ERROR_IF_DEBUG();
// TODO: Use GL_KHR_no_error or a debug callback, where supported?
if (allocatedTextures) {
if (false && allocatedTextures) {
// Users may use replacements or scaling, with high render resolutions, and run out of VRAM.
// This detects that, rather than looking like PPSSPP is broken.
// Calling glGetError() isn't great, but at the end of init shouldn't be too bad...
// Calling glGetError() isn't great, but at the end of init, only after creating textures, shouldn't be too bad...
GLenum err = glGetError();
if (err == GL_OUT_OF_MEMORY) {
WARN_LOG_REPORT(G3D, "GL ran out of GPU memory; switching to low memory mode");
sawOutOfMemory_ = true;
} else if (err != GL_NO_ERROR) {
// We checked the err anyway, might as well log if there is one.
WARN_LOG(G3D, "Got an error after init: %08x (%s)", err, GLEnumToString(err).c_str());
std::string errorString = GLEnumToString(err);
WARN_LOG(G3D, "Got an error after init: %08x (%s)", err, errorString.c_str());
if (errorCallback_) {
errorCallback_("GL frame init error", errorString.c_str(), errorCallbackUserData_);
}
}
}
@@ -425,7 +438,7 @@ void GLQueueRunner::InitCreateFramebuffer(const GLRInitStep &step) {
tex.wrapT = GL_CLAMP_TO_EDGE;
tex.magFilter = linear ? GL_LINEAR : GL_NEAREST;
tex.minFilter = linear ? GL_LINEAR : GL_NEAREST;
tex.canWrap = isPowerOf2(fbo->width) && isPowerOf2(fbo->height);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, tex.wrapS);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tex.wrapT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tex.magFilter);
@@ -617,6 +630,7 @@ void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCal
};
int invalidateAllMask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
/*
for (int j = 0; j < (int)steps.size() - 1; ++j) {
GLRStep &primaryStep = *steps[j];
if (primaryStep.stepType == GLRStepType::RENDER) {
@@ -648,7 +662,7 @@ void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCal
primaryStep.commands.push_back(data);
}
}
}
}*/
CHECK_GL_ERROR_IF_DEBUG();
size_t renderCount = 0;
@@ -982,6 +996,32 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
CHECK_GL_ERROR_IF_DEBUG();
break;
}
case GLRRenderCommand::UNIFORM4UI:
{
_dbg_assert_(curProgram);
int loc = c.uniform4.loc ? *c.uniform4.loc : -1;
if (c.uniform4.name) {
loc = curProgram->GetUniformLoc(c.uniform4.name);
}
if (loc >= 0) {
switch (c.uniform4.count) {
case 1:
glUniform1uiv(loc, 1, (GLuint *)&c.uniform4.v[0]);
break;
case 2:
glUniform2uiv(loc, 1, (GLuint *)c.uniform4.v);
break;
case 3:
glUniform3uiv(loc, 1, (GLuint *)c.uniform4.v);
break;
case 4:
glUniform4uiv(loc, 1, (GLuint *)c.uniform4.v);
break;
}
}
CHECK_GL_ERROR_IF_DEBUG();
break;
}
case GLRRenderCommand::UNIFORM4I:
{
_dbg_assert_(curProgram);
@@ -1423,7 +1463,6 @@ void GLQueueRunner::PerformReadback(const GLRStep &pass) {
if (convert && tempBuffer_ && readbackBuffer_) {
ConvertFromRGBA8888(readbackBuffer_, tempBuffer_, pixelStride, pixelStride, rect.w, rect.h, pass.readback.dstFormat);
}
CHECK_GL_ERROR_IF_DEBUG();
}
+11 -1
View File
@@ -6,6 +6,7 @@
#include "Common/GPU/OpenGL/GLCommon.h"
#include "Common/GPU/DataFormat.h"
#include "Common/GPU/Shader.h"
#include "Common/Data/Collections/TinySet.h"
struct GLRViewport {
@@ -41,6 +42,7 @@ enum class GLRRenderCommand : uint8_t {
BLENDCOLOR,
LOGICOP,
UNIFORM4I,
UNIFORM4UI,
UNIFORM4F,
UNIFORMMATRIX,
TEXTURESAMPLER,
@@ -136,7 +138,7 @@ struct GLRRenderData {
int16_t scissorY;
int16_t scissorW;
int16_t scissorH;
} clear;
} clear; // also used for invalidate
struct {
int slot;
GLRTexture *texture;
@@ -341,6 +343,11 @@ class GLQueueRunner {
public:
GLQueueRunner() {}
void SetErrorCallback(ErrorCallbackFn callback, void *userdata) {
errorCallback_ = callback;
errorCallbackUserData_ = userdata;
}
void RunInitSteps(const std::vector<GLRInitStep> &steps, bool skipGLCalls);
void RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCalls);
@@ -423,4 +430,7 @@ private:
bool sawOutOfMemory_ = false;
bool useDebugGroups_ = false;
ErrorCallbackFn errorCallback_ = nullptr;
void *errorCallbackUserData_ = nullptr;
};
+18
View File
@@ -5,6 +5,7 @@
#include "Common/Log.h"
#include "Common/MemoryUtil.h"
#include "Common/Math/math_util.h"
#if 0 // def _DEBUG
#define VLOG(...) INFO_LOG(G3D, __VA_ARGS__)
@@ -19,6 +20,23 @@ static bool OnRenderThread() {
}
#endif
GLRTexture::GLRTexture(int width, int height, int numMips) {
if (gl_extensions.OES_texture_npot) {
canWrap = true;
} else {
canWrap = isPowerOf2(width) && isPowerOf2(height);
}
w = width;
h = height;
this->numMips = numMips;
}
GLRTexture::~GLRTexture() {
if (texture) {
glDeleteTextures(1, &texture);
}
}
void GLDeleter::Take(GLDeleter &other) {
_assert_msg_(IsEmpty(), "Deleter already has stuff");
shaders = std::move(other.shaders);
+39 -8
View File
@@ -23,13 +23,12 @@ class DrawContext;
class GLRTexture {
public:
~GLRTexture() {
if (texture) {
glDeleteTextures(1, &texture);
}
}
GLRTexture(int width, int height, int numMips);
~GLRTexture();
GLuint texture = 0;
uint16_t w;
uint16_t h;
// We don't trust OpenGL defaults - setting wildly off values ensures that we'll end up overwriting these parameters.
GLenum target = 0xFFFF;
@@ -37,6 +36,7 @@ public:
GLenum wrapT = 0xFFFF;
GLenum magFilter = 0xFFFF;
GLenum minFilter = 0xFFFF;
uint8_t numMips = 0;
bool canWrap = true;
float anisotropy = -100000.0f;
float minLod = -1000.0f;
@@ -47,7 +47,8 @@ public:
class GLRFramebuffer {
public:
GLRFramebuffer(int _width, int _height, bool z_stencil)
: width(_width), height(_height), z_stencil_(z_stencil) {
: width(_width), height(_height), z_stencil_(z_stencil),
color_texture(_width, _height, 1), z_stencil_texture(_width, _height, 1) {
}
~GLRFramebuffer();
@@ -357,6 +358,10 @@ public:
GLRenderManager();
~GLRenderManager();
void SetErrorCallback(ErrorCallbackFn callback, void *userdata) {
queueRunner_.SetErrorCallback(callback, userdata);
}
void ThreadStart(Draw::DrawContext *draw);
void ThreadEnd();
bool ThreadFrame(); // Returns false to request exiting the loop.
@@ -374,9 +379,11 @@ public:
void WaitUntilQueueIdle();
// Creation commands. These were not needed in Vulkan since there we can do that on the main thread.
GLRTexture *CreateTexture(GLenum target) {
// We pass in width/height here even though it's not strictly needed until we support glTextureStorage
// and then we'll also need formats and stuff.
GLRTexture *CreateTexture(GLenum target, int width, int height, int numMips) {
GLRInitStep step{ GLRInitStepType::CREATE_TEXTURE };
step.create_texture.texture = new GLRTexture();
step.create_texture.texture = new GLRTexture(width, height, numMips);
step.create_texture.texture->target = target;
initSteps_.push_back(step);
return step.create_texture.texture;
@@ -655,6 +662,30 @@ public:
curRenderStep_->commands.push_back(data);
}
void SetUniformUI(const GLint *loc, int count, const uint32_t *udata) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
#ifdef _DEBUG
_dbg_assert_(curProgram_);
#endif
GLRRenderData data{ GLRRenderCommand::UNIFORM4UI };
data.uniform4.loc = loc;
data.uniform4.count = count;
memcpy(data.uniform4.v, udata, sizeof(uint32_t) * count);
curRenderStep_->commands.push_back(data);
}
void SetUniformUI1(const GLint *loc, uint32_t udata) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
#ifdef _DEBUG
_dbg_assert_(curProgram_);
#endif
GLRRenderData data{ GLRRenderCommand::UNIFORM4UI };
data.uniform4.loc = loc;
data.uniform4.count = 1;
memcpy(data.uniform4.v, &udata, sizeof(udata));
curRenderStep_->commands.push_back(data);
}
void SetUniformF(const GLint *loc, int count, const float *udata) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER);
#ifdef _DEBUG
+146 -69
View File
@@ -8,6 +8,7 @@
#include "Common/Math/math_util.h"
#include "Common/Math/lin/matrix4x4.h"
#include "Common/GPU/thin3d.h"
#include "Common/GPU/Shader.h"
#include "Common/GPU/OpenGL/DataFormatGL.h"
#include "Common/GPU/OpenGL/GLCommon.h"
#include "Common/GPU/OpenGL/GLDebugLog.h"
@@ -133,25 +134,6 @@ static const unsigned short primToGL[] = {
GL_TRIANGLES,
GL_TRIANGLE_STRIP,
GL_TRIANGLE_FAN,
#if !defined(USING_GLES2) // TODO: Remove when we have better headers
GL_PATCHES,
GL_LINES_ADJACENCY,
GL_LINE_STRIP_ADJACENCY,
GL_TRIANGLES_ADJACENCY,
GL_TRIANGLE_STRIP_ADJACENCY,
#elif !defined(IOS)
GL_POINTS,
GL_POINTS,
GL_POINTS,
GL_POINTS,
GL_POINTS,
#else
GL_POINTS,
GL_POINTS,
GL_POINTS,
GL_POINTS,
GL_POINTS,
#endif
};
class OpenGLBuffer;
@@ -213,14 +195,12 @@ public:
GLuint ShaderStageToOpenGL(ShaderStage stage) {
switch (stage) {
case ShaderStage::VERTEX: return GL_VERTEX_SHADER;
case ShaderStage::Vertex: return GL_VERTEX_SHADER;
#ifndef USING_GLES2
case ShaderStage::COMPUTE: return GL_COMPUTE_SHADER;
case ShaderStage::EVALUATION: return GL_TESS_EVALUATION_SHADER;
case ShaderStage::CONTROL: return GL_TESS_CONTROL_SHADER;
case ShaderStage::GEOMETRY: return GL_GEOMETRY_SHADER;
case ShaderStage::Compute: return GL_COMPUTE_SHADER;
case ShaderStage::Geometry: return GL_GEOMETRY_SHADER;
#endif
case ShaderStage::FRAGMENT:
case ShaderStage::Fragment:
default:
return GL_FRAGMENT_SHADER;
}
@@ -254,7 +234,7 @@ public:
private:
GLRenderManager *render_;
ShaderStage stage_;
ShaderLanguage language_ = ShaderLanguage::GLSL_ES_200;
ShaderLanguage language_ = GLSL_1xx;
GLRShader *shader_ = nullptr;
GLuint glstage_ = 0;
std::string source_; // So we can recompile in case of context loss.
@@ -308,7 +288,7 @@ public:
bool LinkShaders();
bool RequiresBuffer() override {
return inputLayout->RequiresBuffer();
return inputLayout && inputLayout->RequiresBuffer();
}
GLuint prim = 0;
@@ -345,13 +325,19 @@ public:
return caps_;
}
uint32_t GetSupportedShaderLanguages() const override {
if (gl_extensions.IsGLES)
return (uint32_t)ShaderLanguage::GLSL_ES_200 | (uint32_t)ShaderLanguage::GLSL_ES_300;
else
return (uint32_t)ShaderLanguage::GLSL_ES_200 | (uint32_t)ShaderLanguage::GLSL_410;
if (gl_extensions.GLES3) {
return (uint32_t)(ShaderLanguage::GLSL_3xx | ShaderLanguage::GLSL_1xx);
} else {
return (uint32_t)ShaderLanguage::GLSL_1xx;
}
}
uint32_t GetDataFormatSupport(DataFormat fmt) const override;
void SetErrorCallback(ErrorCallbackFn callback, void *userdata) override {
renderManager_.SetErrorCallback(callback, userdata);
}
DepthStencilState *CreateDepthStencilState(const DepthStencilStateDesc &desc) override;
BlendState *CreateBlendState(const BlendStateDesc &desc) override;
SamplerState *CreateSamplerState(const SamplerStateDesc &desc) override;
@@ -381,7 +367,7 @@ public:
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
void BindSamplerStates(int start, int count, SamplerState **states) override {
if (start + count >= MAX_TEXTURE_SLOTS) {
if (start + count > MAX_TEXTURE_SLOTS) {
return;
}
for (int i = 0; i < count; i++) {
@@ -492,7 +478,9 @@ private:
// Bound state
OpenGLSamplerState *boundSamplers_[MAX_TEXTURE_SLOTS]{};
OpenGLTexture *boundTextures_[MAX_TEXTURE_SLOTS]{};
// Point to GLRTexture directly because they can point to the textures
// in framebuffers too (which also can be bound).
const GLRTexture *boundTextures_[MAX_TEXTURE_SLOTS]{};
OpenGLPipeline *curPipeline_ = nullptr;
OpenGLBuffer *curVBuffers_[4]{};
@@ -597,6 +585,76 @@ OpenGLContext::OpenGLContext() {
// TODO: Should this workaround be removed for newer devices/drivers?
bugs_.Infest(Bugs::PVR_GENMIPMAP_HEIGHT_GREATER);
}
shaderLanguageDesc_.Init(GLSL_1xx);
shaderLanguageDesc_.glslVersionNumber = gl_extensions.GLSLVersion();
snprintf(shaderLanguageDesc_.driverInfo, sizeof(shaderLanguageDesc_.driverInfo),
"%s - GLSL %d", gl_extensions.model, gl_extensions.GLSLVersion());
// Detect shader language features.
if (gl_extensions.IsGLES) {
shaderLanguageDesc_.gles = true;
if (gl_extensions.GLES3) {
shaderLanguageDesc_.shaderLanguage = ShaderLanguage::GLSL_3xx;
shaderLanguageDesc_.fragColor0 = "fragColor0";
shaderLanguageDesc_.texture = "texture";
shaderLanguageDesc_.glslES30 = true;
shaderLanguageDesc_.bitwiseOps = true;
shaderLanguageDesc_.texelFetch = "texelFetch";
shaderLanguageDesc_.varying_vs = "out";
shaderLanguageDesc_.varying_fs = "in";
shaderLanguageDesc_.attribute = "in";
} else {
shaderLanguageDesc_.shaderLanguage = ShaderLanguage::GLSL_1xx;
if (gl_extensions.EXT_gpu_shader4) {
shaderLanguageDesc_.bitwiseOps = true;
shaderLanguageDesc_.texelFetch = "texelFetch2D";
}
if (gl_extensions.EXT_blend_func_extended) {
// Oldy moldy GLES, so use the fixed output name.
shaderLanguageDesc_.fragColor1 = "gl_SecondaryFragColorEXT";
}
}
} else {
if (gl_extensions.IsCoreContext) {
if (gl_extensions.VersionGEThan(3, 3, 0)) {
shaderLanguageDesc_.shaderLanguage = ShaderLanguage::GLSL_3xx;
shaderLanguageDesc_.fragColor0 = "fragColor0";
shaderLanguageDesc_.texture = "texture";
shaderLanguageDesc_.glslES30 = true;
shaderLanguageDesc_.bitwiseOps = true;
shaderLanguageDesc_.texelFetch = "texelFetch";
shaderLanguageDesc_.varying_vs = "out";
shaderLanguageDesc_.varying_fs = "in";
shaderLanguageDesc_.attribute = "in";
} else if (gl_extensions.VersionGEThan(3, 0, 0)) {
// Hm, I think this is wrong. This should be outside "if (gl_extensions.IsCoreContext)".
shaderLanguageDesc_.shaderLanguage = ShaderLanguage::GLSL_1xx;
shaderLanguageDesc_.fragColor0 = "fragColor0";
shaderLanguageDesc_.bitwiseOps = true;
shaderLanguageDesc_.texelFetch = "texelFetch";
} else {
// This too...
shaderLanguageDesc_.shaderLanguage = ShaderLanguage::GLSL_1xx;
if (gl_extensions.EXT_gpu_shader4) {
shaderLanguageDesc_.bitwiseOps = true;
shaderLanguageDesc_.texelFetch = "texelFetch2D";
}
}
}
}
if (gl_extensions.IsGLES) {
caps_.framebufferFetchSupported = (gl_extensions.EXT_shader_framebuffer_fetch || gl_extensions.ARM_shader_framebuffer_fetch);
if (gl_extensions.EXT_shader_framebuffer_fetch) {
shaderLanguageDesc_.framebufferFetchExtension = "#extension GL_EXT_shader_framebuffer_fetch : require";
shaderLanguageDesc_.lastFragData = gl_extensions.GLES3 ? "fragColor0" : "gl_LastFragData[0]";
} else if (gl_extensions.ARM_shader_framebuffer_fetch) {
shaderLanguageDesc_.framebufferFetchExtension = "#extension GL_ARM_shader_framebuffer_fetch : require";
shaderLanguageDesc_.lastFragData = "gl_LastFragColorARM";
}
}
}
OpenGLContext::~OpenGLContext() {
@@ -663,9 +721,7 @@ public:
bool HasMips() const {
return mipLevels_ > 1 || generatedMips_;
}
bool CanWrap() const {
return canWrap_;
}
TextureType GetType() const { return type_; }
void Bind(int stage) {
render_->BindTexture(stage, tex_);
@@ -673,6 +729,9 @@ public:
int NumMipmaps() const {
return mipLevels_;
}
const GLRTexture *GetTex() const {
return tex_;
}
private:
void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback callback);
@@ -684,7 +743,6 @@ private:
TextureType type_;
int mipLevels_;
bool generatedMips_;
bool canWrap_;
};
OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) : render_(render) {
@@ -695,9 +753,8 @@ OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) :
format_ = desc.format;
type_ = desc.type;
GLenum target = TypeToTarget(desc.type);
tex_ = render->CreateTexture(target);
tex_ = render->CreateTexture(target, desc.width, desc.height, desc.mipLevels);
canWrap_ = isPowerOf2(width_) && isPowerOf2(height_);
mipLevels_ = desc.mipLevels;
if (desc.initData.empty())
return;
@@ -731,15 +788,16 @@ OpenGLTexture::~OpenGLTexture() {
class OpenGLFramebuffer : public Framebuffer {
public:
OpenGLFramebuffer(GLRenderManager *render) : render_(render) {}
OpenGLFramebuffer(GLRenderManager *render, GLRFramebuffer *framebuffer) : render_(render), framebuffer_(framebuffer) {
width_ = framebuffer->width;
height_ = framebuffer->height;
}
~OpenGLFramebuffer() {
render_->DeleteFramebuffer(framebuffer);
render_->DeleteFramebuffer(framebuffer_);
}
GLRenderManager *render_;
GLRFramebuffer *framebuffer = nullptr;
FBColorDepth colorDepth = FBO_8888;
GLRFramebuffer *framebuffer_ = nullptr;
};
// TODO: SSE/NEON optimize, and move to ColorConv.cpp.
@@ -841,7 +899,7 @@ bool OpenGLContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBit
aspect |= GL_DEPTH_BUFFER_BIT;
if (channelBits & FB_STENCIL_BIT)
aspect |= GL_STENCIL_BUFFER_BIT;
renderManager_.CopyFramebufferToMemorySync(fb ? fb->framebuffer : nullptr, aspect, x, y, w, h, dataFormat, (uint8_t *)pixels, pixelStride, tag);
renderManager_.CopyFramebufferToMemorySync(fb ? fb->framebuffer_ : nullptr, aspect, x, y, w, h, dataFormat, (uint8_t *)pixels, pixelStride, tag);
return true;
}
@@ -972,7 +1030,7 @@ Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
ERROR_LOG(G3D, "Invalid primitive type");
return nullptr;
}
if (!desc.depthStencil || !desc.blend || !desc.raster || !desc.inputLayout) {
if (!desc.depthStencil || !desc.blend || !desc.raster) {
ERROR_LOG(G3D, "Incomplete prim desciption");
return nullptr;
}
@@ -1002,7 +1060,9 @@ Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
pipeline->depthStencil->AddRef();
pipeline->blend->AddRef();
pipeline->raster->AddRef();
pipeline->inputLayout->AddRef();
if (pipeline->inputLayout) {
pipeline->inputLayout->AddRef();
}
return pipeline;
} else {
ERROR_LOG(G3D, "Failed to create pipeline - shaders failed to link");
@@ -1012,7 +1072,7 @@ Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
}
void OpenGLContext::BindTextures(int start, int count, Texture **textures) {
if (start + count >= MAX_TEXTURE_SLOTS) {
if (start + count > MAX_TEXTURE_SLOTS) {
return;
}
for (int i = start; i < start + count; i++) {
@@ -1023,14 +1083,14 @@ void OpenGLContext::BindTextures(int start, int count, Texture **textures) {
continue;
}
glTex->Bind(i);
boundTextures_[i] = glTex;
boundTextures_[i] = glTex->GetTex();
}
}
void OpenGLContext::ApplySamplers() {
for (int i = 0; i < MAX_TEXTURE_SLOTS; i++) {
const OpenGLSamplerState *samp = boundSamplers_[i];
const OpenGLTexture *tex = boundTextures_[i];
const GLRTexture *tex = boundTextures_[i];
if (tex) {
_assert_(samp);
} else {
@@ -1038,7 +1098,7 @@ void OpenGLContext::ApplySamplers() {
}
GLenum wrapS;
GLenum wrapT;
if (tex->CanWrap()) {
if (tex->canWrap) {
wrapS = samp->wrapU;
wrapT = samp->wrapV;
} else {
@@ -1046,9 +1106,9 @@ void OpenGLContext::ApplySamplers() {
wrapT = GL_CLAMP_TO_EDGE;
}
GLenum magFilt = samp->magFilt;
GLenum minFilt = tex->HasMips() ? samp->mipMinFilt : samp->minFilt;
GLenum minFilt = tex->numMips > 1 ? samp->mipMinFilt : samp->minFilt;
renderManager_.SetTextureSampler(i, wrapS, wrapT, magFilt, minFilt, 0.0f);
renderManager_.SetTextureLod(i, 0.0, (float)(tex->NumMipmaps() - 1), 0.0);
renderManager_.SetTextureLod(i, 0.0, (float)(tex->numMips - 1), 0.0);
}
}
@@ -1140,7 +1200,9 @@ void OpenGLContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) {
void OpenGLContext::Draw(int vertexCount, int offset) {
_dbg_assert_msg_(curVBuffers_[0], "Can't call Draw without a vertex buffer");
ApplySamplers();
renderManager_.BindVertexBuffer(curPipeline_->inputLayout->inputLayout_, curVBuffers_[0]->buffer_, curVBufferOffsets_[0]);
if (curPipeline_->inputLayout) {
renderManager_.BindVertexBuffer(curPipeline_->inputLayout->inputLayout_, curVBuffers_[0]->buffer_, curVBufferOffsets_[0]);
}
renderManager_.Draw(curPipeline_->prim, offset, vertexCount);
}
@@ -1148,12 +1210,15 @@ void OpenGLContext::DrawIndexed(int vertexCount, int offset) {
_dbg_assert_msg_(curVBuffers_[0], "Can't call DrawIndexed without a vertex buffer");
_dbg_assert_msg_(curIBuffer_, "Can't call DrawIndexed without an index buffer");
ApplySamplers();
renderManager_.BindVertexBuffer(curPipeline_->inputLayout->inputLayout_, curVBuffers_[0]->buffer_, curVBufferOffsets_[0]);
if (curPipeline_->inputLayout) {
renderManager_.BindVertexBuffer(curPipeline_->inputLayout->inputLayout_, curVBuffers_[0]->buffer_, curVBufferOffsets_[0]);
}
renderManager_.BindIndexBuffer(curIBuffer_->buffer_);
renderManager_.DrawIndexed(curPipeline_->prim, vertexCount, GL_UNSIGNED_SHORT, (void *)((intptr_t)curIBufferOffset_ + offset * sizeof(uint32_t)));
}
void OpenGLContext::DrawUP(const void *vdata, int vertexCount) {
_assert_(curPipeline_->inputLayout);
int stride = curPipeline_->inputLayout->stride;
size_t dataSize = stride * vertexCount;
@@ -1163,7 +1228,9 @@ void OpenGLContext::DrawUP(const void *vdata, int vertexCount) {
size_t offset = frameData.push->Push(vdata, dataSize, &buf);
ApplySamplers();
renderManager_.BindVertexBuffer(curPipeline_->inputLayout->inputLayout_, buf, offset);
if (curPipeline_->inputLayout) {
renderManager_.BindVertexBuffer(curPipeline_->inputLayout->inputLayout_, buf, offset);
}
renderManager_.Draw(curPipeline_->prim, 0, vertexCount);
}
@@ -1194,7 +1261,7 @@ OpenGLInputLayout::~OpenGLInputLayout() {
void OpenGLInputLayout::Compile(const InputLayoutDesc &desc) {
// TODO: This is only accurate if there's only one stream. But whatever, for now we
// never use multiple streams anyway.
stride = (GLsizei)desc.bindings[0].stride;
stride = desc.bindings.empty() ? 0 : (GLsizei)desc.bindings[0].stride;
std::vector<GLRInputLayout::Entry> entries;
for (auto &attr : desc.attributes) {
@@ -1231,14 +1298,18 @@ void OpenGLInputLayout::Compile(const InputLayoutDesc &desc) {
entries.push_back(entry);
}
inputLayout_ = render_->CreateInputLayout(entries);
if (!entries.empty()) {
inputLayout_ = render_->CreateInputLayout(entries);
} else {
inputLayout_ = nullptr;
}
}
Framebuffer *OpenGLContext::CreateFramebuffer(const FramebufferDesc &desc) {
CheckGLExtensions();
OpenGLFramebuffer *fbo = new OpenGLFramebuffer(&renderManager_);
fbo->framebuffer = renderManager_.CreateFramebuffer(desc.width, desc.height, desc.z_stencil);
GLRFramebuffer *framebuffer = renderManager_.CreateFramebuffer(desc.width, desc.height, desc.z_stencil);
OpenGLFramebuffer *fbo = new OpenGLFramebuffer(&renderManager_, framebuffer);
return fbo;
}
@@ -1248,7 +1319,7 @@ void OpenGLContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const Render
GLRRenderPassAction depth = (GLRRenderPassAction)rp.depth;
GLRRenderPassAction stencil = (GLRRenderPassAction)rp.stencil;
renderManager_.BindFramebufferAsRenderTarget(fb ? fb->framebuffer : nullptr, color, depth, stencil, rp.clearColor, rp.clearDepth, rp.clearStencil, tag);
renderManager_.BindFramebufferAsRenderTarget(fb ? fb->framebuffer_ : nullptr, color, depth, stencil, rp.clearColor, rp.clearDepth, rp.clearStencil, tag);
}
void OpenGLContext::CopyFramebufferImage(Framebuffer *fbsrc, int srcLevel, int srcX, int srcY, int srcZ, Framebuffer *fbdst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) {
@@ -1264,7 +1335,7 @@ void OpenGLContext::CopyFramebufferImage(Framebuffer *fbsrc, int srcLevel, int s
if (channelBits & FB_STENCIL_BIT)
aspect |= GL_STENCIL_BUFFER_BIT;
}
renderManager_.CopyFramebuffer(src->framebuffer, GLRect2D{ srcX, srcY, width, height }, dst->framebuffer, GLOffset2D{ dstX, dstY }, aspect, tag);
renderManager_.CopyFramebuffer(src->framebuffer_, GLRect2D{ srcX, srcY, width, height }, dst->framebuffer_, GLOffset2D{ dstX, dstY }, aspect, tag);
}
bool OpenGLContext::BlitFramebuffer(Framebuffer *fbsrc, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *fbdst, int dstX1, int dstY1, int dstX2, int dstY2, int channels, FBBlitFilter linearFilter, const char *tag) {
@@ -1278,7 +1349,7 @@ bool OpenGLContext::BlitFramebuffer(Framebuffer *fbsrc, int srcX1, int srcY1, in
if (channels & FB_STENCIL_BIT)
aspect |= GL_STENCIL_BUFFER_BIT;
renderManager_.BlitFramebuffer(src->framebuffer, GLRect2D{ srcX1, srcY1, srcX2 - srcX1, srcY2 - srcY1 }, dst->framebuffer, GLRect2D{ dstX1, dstY1, dstX2 - dstX1, dstY2 - dstY1 }, aspect, linearFilter == FB_BLIT_LINEAR, tag);
renderManager_.BlitFramebuffer(src->framebuffer_, GLRect2D{ srcX1, srcY1, srcX2 - srcX1, srcY2 - srcY1 }, dst->framebuffer_, GLRect2D{ dstX1, dstY1, dstX2 - dstX1, dstY2 - dstY1 }, aspect, linearFilter == FB_BLIT_LINEAR, tag);
return true;
}
@@ -1286,20 +1357,26 @@ void OpenGLContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBCh
OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo;
GLuint aspect = 0;
if (channelBit & FB_COLOR_BIT)
if (channelBit & FB_COLOR_BIT) {
aspect |= GL_COLOR_BUFFER_BIT;
if (channelBit & FB_DEPTH_BIT)
boundTextures_[binding] = &fb->framebuffer_->color_texture;
}
if (channelBit & FB_DEPTH_BIT) {
aspect |= GL_DEPTH_BUFFER_BIT;
if (channelBit & FB_STENCIL_BIT)
boundTextures_[binding] = &fb->framebuffer_->z_stencil_texture;
}
if (channelBit & FB_STENCIL_BIT) {
aspect |= GL_STENCIL_BUFFER_BIT;
renderManager_.BindFramebufferAsTexture(fb->framebuffer, binding, aspect, color);
boundTextures_[binding] = &fb->framebuffer_->z_stencil_texture;
}
renderManager_.BindFramebufferAsTexture(fb->framebuffer_, binding, aspect, color);
}
void OpenGLContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) {
OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo;
if (fb) {
*w = fb->framebuffer->width;
*h = fb->framebuffer->height;
*w = fb->Width();
*h = fb->Height();
} else {
*w = targetWidth_;
*h = targetHeight_;
+186
View File
@@ -0,0 +1,186 @@
#include "Common/GPU/Shader.h"
#ifdef USE_CRT_DBG
#undef new
#endif
#include "ext/glslang/SPIRV/GlslangToSpv.h"
ShaderLanguageDesc::ShaderLanguageDesc(ShaderLanguage lang) {
Init(lang);
}
void ShaderLanguageDesc::Init(ShaderLanguage lang) {
shaderLanguage = lang;
switch (lang) {
case GLSL_1xx:
// Just used in the shader test, and as a basis for the others in DetectShaderLanguage.
// The real OpenGL initialization happens in thin3d_gl.cpp.
glslVersionNumber = 110;
attribute = "attribute";
varying_vs = "varying";
varying_fs = "varying";
fragColor0 = "gl_FragColor";
fragColor1 = "fragColor1";
texture = "texture2D";
texelFetch = nullptr;
bitwiseOps = false;
lastFragData = nullptr;
gles = false;
forceMatrix4x4 = true;
break;
case GLSL_3xx:
// Just used in the shader test.
glslVersionNumber = 300; // GLSL ES 3.0
varying_vs = "out";
varying_fs = "in";
attribute = "in";
fragColor0 = "fragColor0";
fragColor1 = "fragColor1";
texture = "texture";
texelFetch = "texelFetch";
bitwiseOps = true;
lastFragData = nullptr;
gles = true;
forceMatrix4x4 = true;
glslES30 = true;
texelFetch = "texelFetch";
break;
case GLSL_VULKAN:
fragColor0 = "fragColor0";
fragColor1 = "fragColor1";
varying_fs = "in";
varying_vs = "out";
attribute = "in";
bitwiseOps = true;
framebufferFetchExtension = nullptr;
gles = false;
glslES30 = true;
glslVersionNumber = 450;
lastFragData = nullptr;
texture = "texture";
texelFetch = "texelFetch";
forceMatrix4x4 = false;
coefsFromBuffers = true;
break;
case HLSL_D3D9:
case HLSL_D3D11:
if (lang == HLSL_D3D11) {
fragColor0 = "outfragment.target";
fragColor1 = "outfragment.target1";
} else {
fragColor0 = "target";
}
varying_fs = "in";
varying_vs = "out";
attribute = "in";
bitwiseOps = lang == HLSL_D3D11;
framebufferFetchExtension = nullptr;
gles = false;
glslES30 = true; // Hm, D3D9 too?
glslVersionNumber = 0;
lastFragData = nullptr;
texture = "texture";
texelFetch = "texelFetch";
forceMatrix4x4 = false;
coefsFromBuffers = true;
vsOutPrefix = "Out.";
viewportYSign = "-";
break;
}
}
void init_resources(TBuiltInResource &Resources) {
Resources.maxLights = 32;
Resources.maxClipPlanes = 6;
Resources.maxTextureUnits = 32;
Resources.maxTextureCoords = 32;
Resources.maxVertexAttribs = 64;
Resources.maxVertexUniformComponents = 4096;
Resources.maxVaryingFloats = 64;
Resources.maxVertexTextureImageUnits = 32;
Resources.maxCombinedTextureImageUnits = 80;
Resources.maxTextureImageUnits = 32;
Resources.maxFragmentUniformComponents = 4096;
Resources.maxDrawBuffers = 32;
Resources.maxVertexUniformVectors = 128;
Resources.maxVaryingVectors = 8;
Resources.maxFragmentUniformVectors = 16;
Resources.maxVertexOutputVectors = 16;
Resources.maxFragmentInputVectors = 15;
Resources.minProgramTexelOffset = -8;
Resources.maxProgramTexelOffset = 7;
Resources.maxClipDistances = 8;
Resources.maxComputeWorkGroupCountX = 65535;
Resources.maxComputeWorkGroupCountY = 65535;
Resources.maxComputeWorkGroupCountZ = 65535;
Resources.maxComputeWorkGroupSizeX = 1024;
Resources.maxComputeWorkGroupSizeY = 1024;
Resources.maxComputeWorkGroupSizeZ = 64;
Resources.maxComputeUniformComponents = 1024;
Resources.maxComputeTextureImageUnits = 16;
Resources.maxComputeImageUniforms = 8;
Resources.maxComputeAtomicCounters = 8;
Resources.maxComputeAtomicCounterBuffers = 1;
Resources.maxVaryingComponents = 60;
Resources.maxVertexOutputComponents = 64;
Resources.maxGeometryInputComponents = 64;
Resources.maxGeometryOutputComponents = 128;
Resources.maxFragmentInputComponents = 128;
Resources.maxImageUnits = 8;
Resources.maxCombinedImageUnitsAndFragmentOutputs = 8;
Resources.maxCombinedShaderOutputResources = 8;
Resources.maxImageSamples = 0;
Resources.maxVertexImageUniforms = 0;
Resources.maxTessControlImageUniforms = 0;
Resources.maxTessEvaluationImageUniforms = 0;
Resources.maxGeometryImageUniforms = 0;
Resources.maxFragmentImageUniforms = 8;
Resources.maxCombinedImageUniforms = 8;
Resources.maxGeometryTextureImageUnits = 16;
Resources.maxGeometryOutputVertices = 256;
Resources.maxGeometryTotalOutputComponents = 1024;
Resources.maxGeometryUniformComponents = 1024;
Resources.maxGeometryVaryingComponents = 64;
Resources.maxTessControlInputComponents = 128;
Resources.maxTessControlOutputComponents = 128;
Resources.maxTessControlTextureImageUnits = 16;
Resources.maxTessControlUniformComponents = 1024;
Resources.maxTessControlTotalOutputComponents = 4096;
Resources.maxTessEvaluationInputComponents = 128;
Resources.maxTessEvaluationOutputComponents = 128;
Resources.maxTessEvaluationTextureImageUnits = 16;
Resources.maxTessEvaluationUniformComponents = 1024;
Resources.maxTessPatchComponents = 120;
Resources.maxPatchVertices = 32;
Resources.maxTessGenLevel = 64;
Resources.maxViewports = 16;
Resources.maxVertexAtomicCounters = 0;
Resources.maxTessControlAtomicCounters = 0;
Resources.maxTessEvaluationAtomicCounters = 0;
Resources.maxGeometryAtomicCounters = 0;
Resources.maxFragmentAtomicCounters = 8;
Resources.maxCombinedAtomicCounters = 8;
Resources.maxAtomicCounterBindings = 1;
Resources.maxVertexAtomicCounterBuffers = 0;
Resources.maxTessControlAtomicCounterBuffers = 0;
Resources.maxTessEvaluationAtomicCounterBuffers = 0;
Resources.maxGeometryAtomicCounterBuffers = 0;
Resources.maxFragmentAtomicCounterBuffers = 1;
Resources.maxCombinedAtomicCounterBuffers = 1;
Resources.maxAtomicCounterBufferSize = 16384;
Resources.maxTransformFeedbackBuffers = 4;
Resources.maxTransformFeedbackInterleavedComponents = 64;
Resources.maxCullDistances = 8;
Resources.maxCombinedClipAndCullDistances = 8;
Resources.maxSamples = 4;
Resources.limits.nonInductiveForLoops = 1;
Resources.limits.whileLoops = 1;
Resources.limits.doWhileLoops = 1;
Resources.limits.generalUniformIndexing = 1;
Resources.limits.generalAttributeMatrixVectorIndexing = 1;
Resources.limits.generalVaryingIndexing = 1;
Resources.limits.generalSamplerIndexing = 1;
Resources.limits.generalVariableIndexing = 1;
Resources.limits.generalConstantMatrixVectorIndexing = 1;
}
+83
View File
@@ -0,0 +1,83 @@
#pragma once
#include <vector>
#include <cstdint>
#include <cstddef> // for size_t
// GLSL_1xx and GLSL_3xx each cover a lot of sub variants. All the little quirks
// that differ are covered in ShaderLanguageDesc.
// Defined as a bitmask so stuff like GetSupportedShaderLanguages can return combinations.
enum ShaderLanguage {
GLSL_1xx = 1,
GLSL_3xx = 2,
GLSL_VULKAN = 4,
HLSL_D3D9 = 8,
HLSL_D3D11 = 16,
};
inline bool ShaderLanguageIsOpenGL(ShaderLanguage lang) {
return lang == GLSL_1xx || lang == GLSL_3xx;
}
enum class ShaderStage {
Vertex,
Fragment,
Geometry,
Compute,
};
struct ShaderLanguageDesc {
ShaderLanguageDesc() {}
explicit ShaderLanguageDesc(ShaderLanguage lang);
void Init(ShaderLanguage lang);
int glslVersionNumber = 0;
ShaderLanguage shaderLanguage;
bool gles = false;
const char *varying_fs = nullptr;
const char *varying_vs = nullptr;
const char *attribute = nullptr;
const char *fragColor0 = nullptr;
const char *fragColor1 = nullptr;
const char *texture = nullptr;
const char *texelFetch = nullptr;
const char *lastFragData = nullptr;
const char *framebufferFetchExtension = nullptr;
const char *vsOutPrefix = "";
const char *viewportYSign = "";
bool glslES30 = false; // really glslES30Features. TODO: Clean this up.
bool bitwiseOps = false;
bool forceMatrix4x4 = false;
bool coefsFromBuffers = false;
char driverInfo[128]; // Really only GL uses this.
};
enum class UniformType : int8_t {
FLOAT1,
FLOAT2,
FLOAT3,
FLOAT4,
MATRIX4X4,
};
// Describe uniforms intricately enough that we can support them on all backends.
// This will generate a uniform struct on the newer backends and individual uniforms on the older ones.
struct UniformDesc {
const char *name; // For GL
int16_t vertexReg; // For D3D
int16_t fragmentReg; // For D3D
UniformType type;
int16_t offset;
// TODO: Support array elements etc.
};
struct UniformBufferDesc {
size_t uniformBufferSize;
std::vector<UniformDesc> uniforms;
};
// For passing error messages from shader compilation (and other critical issues) back to the host.
// This can run on any thread - be aware!
// TODO: See if we can find a less generic name for this.
typedef void (*ErrorCallbackFn)(const char *shortDesc, const char *details, void *userdata);
@@ -36,8 +36,9 @@
#include "Common/Log.h"
#include "Common/StringUtils.h"
#include "Common/GPU/Shader.h"
#include "GPU/Common/ShaderTranslation.h"
#include "Common/GPU/ShaderTranslation.h"
#include "ext/glslang/SPIRV/GlslangToSpv.h"
#include "Common/GPU/thin3d.h"
#include "Common/GPU/OpenGL/GLFeatures.h"
@@ -52,14 +53,12 @@
extern void init_resources(TBuiltInResource &Resources);
static EShLanguage GetLanguage(const Draw::ShaderStage stage) {
static EShLanguage GetLanguage(const ShaderStage stage) {
switch (stage) {
case Draw::ShaderStage::VERTEX: return EShLangVertex;
case Draw::ShaderStage::CONTROL: return EShLangTessControl;
case Draw::ShaderStage::EVALUATION: return EShLangTessEvaluation;
case Draw::ShaderStage::GEOMETRY: return EShLangGeometry;
case Draw::ShaderStage::FRAGMENT: return EShLangFragment;
case Draw::ShaderStage::COMPUTE: return EShLangCompute;
case ShaderStage::Vertex: return EShLangVertex;
case ShaderStage::Geometry: return EShLangGeometry;
case ShaderStage::Fragment: return EShLangFragment;
case ShaderStage::Compute: return EShLangCompute;
default: return EShLangVertex;
}
}
@@ -76,7 +75,7 @@ void ShaderTranslationShutdown() {
#endif
}
std::string Preprocess(std::string code, ShaderLanguage lang, Draw::ShaderStage stage) {
std::string Preprocess(std::string code, ShaderLanguage lang, ShaderStage stage) {
// This takes GL up to the version we need.
return code;
}
@@ -125,8 +124,8 @@ float u_video : register(c5);
// Also we need to rip out single uniforms and replace them with blocks.
// Should probably do it in the source shader instead and then back translate to old style GLSL, but
// SPIRV-Cross currently won't compile with the Android NDK so I can't be bothered.
std::string Postprocess(std::string code, ShaderLanguage lang, Draw::ShaderStage stage) {
if (lang != HLSL_D3D11 && lang != HLSL_DX9)
std::string Postprocess(std::string code, ShaderLanguage lang, ShaderStage stage) {
if (lang != HLSL_D3D11 && lang != HLSL_D3D9)
return code;
std::stringstream out;
@@ -134,18 +133,18 @@ std::string Postprocess(std::string code, ShaderLanguage lang, Draw::ShaderStage
// Output the uniform buffer.
if (lang == HLSL_D3D11)
out << cbufferDecl;
else if (lang == HLSL_DX9)
else if (lang == HLSL_D3D9)
out << d3d9RegisterDecl;
// Alright, now let's go through it line by line and zap the single uniforms.
std::string line;
std::stringstream instream(code);
while (std::getline(instream, line)) {
if (line == "uniform sampler2D sampler0;" && lang == HLSL_DX9) {
if (line == "uniform sampler2D sampler0;" && lang == HLSL_D3D9) {
out << "sampler2D sampler0 : register(s0);\n";
continue;
}
if (line == "uniform sampler2D sampler1;" && lang == HLSL_DX9) {
if (line == "uniform sampler2D sampler1;" && lang == HLSL_D3D9) {
out << "sampler2D sampler1 : register(s1);\n";
continue;
}
@@ -158,24 +157,24 @@ std::string Postprocess(std::string code, ShaderLanguage lang, Draw::ShaderStage
return output;
}
bool ConvertToVulkanGLSL(std::string *dest, TranslatedShaderMetadata *destMetadata, std::string src, Draw::ShaderStage stage, std::string *errorMessage) {
bool ConvertToVulkanGLSL(std::string *dest, TranslatedShaderMetadata *destMetadata, std::string src, ShaderStage stage, std::string *errorMessage) {
std::stringstream out;
static struct {
Draw::ShaderStage stage;
ShaderStage stage;
const char *needle;
const char *replacement;
} replacements[] = {
{ Draw::ShaderStage::VERTEX, "attribute vec4 a_position;", "layout(location = 0) in vec4 a_position;" },
{ Draw::ShaderStage::VERTEX, "attribute vec2 a_texcoord0;", "layout(location = 2) in vec2 a_texcoord0;"},
{ Draw::ShaderStage::VERTEX, "varying vec2 v_position;", "layout(location = 0) out vec2 v_position;" },
{ Draw::ShaderStage::FRAGMENT, "varying vec2 v_position;", "layout(location = 0) in vec2 v_position;" },
{ Draw::ShaderStage::FRAGMENT, "texture2D(", "texture(" },
{ Draw::ShaderStage::FRAGMENT, "gl_FragColor", "fragColor0" },
{ ShaderStage::Vertex, "attribute vec4 a_position;", "layout(location = 0) in vec4 a_position;" },
{ ShaderStage::Vertex, "attribute vec2 a_texcoord0;", "layout(location = 2) in vec2 a_texcoord0;"},
{ ShaderStage::Vertex, "varying vec2 v_position;", "layout(location = 0) out vec2 v_position;" },
{ ShaderStage::Fragment, "varying vec2 v_position;", "layout(location = 0) in vec2 v_position;" },
{ ShaderStage::Fragment, "texture2D(", "texture(" },
{ ShaderStage::Fragment, "gl_FragColor", "fragColor0" },
};
out << vulkanPrologue;
if (stage == Draw::ShaderStage::FRAGMENT) {
if (stage == ShaderStage::Fragment) {
out << "layout (location = 0) out vec4 fragColor0;\n";
}
// Output the uniform buffer.
@@ -197,7 +196,7 @@ bool ConvertToVulkanGLSL(std::string *dest, TranslatedShaderMetadata *destMetada
} else if (line.find("uniform ") != std::string::npos) {
continue;
} else if (2 == sscanf(line.c_str(), "varying vec%d v_texcoord%d;", &vecSize, &num)) {
if (stage == Draw::ShaderStage::FRAGMENT) {
if (stage == ShaderStage::Fragment) {
line = StringFromFormat("layout(location = %d) in vec%d v_texcoord%d;", num, vecSize, num);
} else {
line = StringFromFormat("layout(location = %d) out vec%d v_texcoord%d;", num, vecSize, num);
@@ -218,11 +217,11 @@ bool ConvertToVulkanGLSL(std::string *dest, TranslatedShaderMetadata *destMetada
return true;
}
bool TranslateShader(std::string *dest, ShaderLanguage destLang, TranslatedShaderMetadata *destMetadata, std::string src, ShaderLanguage srcLang, Draw::ShaderStage stage, std::string *errorMessage) {
if (srcLang != GLSL_300 && srcLang != GLSL_140)
bool TranslateShader(std::string *dest, ShaderLanguage destLang, TranslatedShaderMetadata *destMetadata, std::string src, ShaderLanguage srcLang, ShaderStage stage, std::string *errorMessage) {
if (srcLang != GLSL_3xx && srcLang != GLSL_1xx)
return false;
if ((srcLang == GLSL_140 || srcLang == GLSL_300) && destLang == GLSL_VULKAN) {
if ((srcLang == GLSL_1xx || srcLang == GLSL_3xx) && destLang == GLSL_VULKAN) {
// Let's just mess about at the string level, no need to recompile.
bool result = ConvertToVulkanGLSL(dest, destMetadata, src, stage, errorMessage);
return result;
@@ -291,7 +290,7 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, TranslatedShade
switch (destLang) {
#ifdef _WIN32
case HLSL_DX9:
case HLSL_D3D9:
{
spirv_cross::CompilerHLSL hlsl(spirv);
spirv_cross::CompilerHLSL::Options options{};
@@ -326,7 +325,7 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, TranslatedShade
return true;
}
#endif
case GLSL_140:
case GLSL_1xx:
{
spirv_cross::CompilerGLSL glsl(std::move(spirv));
// The SPIR-V is now parsed, and we can perform reflection on it.
@@ -351,7 +350,7 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, TranslatedShade
*dest = glsl.compile();
return true;
}
case GLSL_300:
case GLSL_3xx:
{
spirv_cross::CompilerGLSL glsl(std::move(spirv));
// The SPIR-V is now parsed, and we can perform reflection on it.
@@ -18,7 +18,8 @@
#pragma once
#include <string>
#include "GPU/Common/ShaderCommon.h"
#include "Common/GPU/Shader.h"
#include "Common/GPU/thin3d.h"
struct TranslatedShaderMetadata {
@@ -28,4 +29,4 @@ struct TranslatedShaderMetadata {
void ShaderTranslationInit();
void ShaderTranslationShutdown();
bool TranslateShader(std::string *dst, ShaderLanguage destLang, TranslatedShaderMetadata *destMetadata, std::string src, ShaderLanguage srcLang, Draw::ShaderStage stage, std::string *errorMessage);
bool TranslateShader(std::string *dst, ShaderLanguage destLang, TranslatedShaderMetadata *destMetadata, std::string src, ShaderLanguage srcLang, ShaderStage stage, std::string *errorMessage);
+313
View File
@@ -0,0 +1,313 @@
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include "Common/GPU/Shader.h"
#include "Common/GPU/ShaderWriter.h"
const char *vulkan_glsl_preamble_fs =
"#version 450\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"#extension GL_ARB_shading_language_420pack : enable\n"
"#extension GL_ARB_conservative_depth : enable\n"
"#extension GL_ARB_shader_image_load_store : enable\n"
"#define splat3(x) vec3(x)\n"
"#define DISCARD discard\n"
"precision lowp float;\n"
"precision highp int;\n"
"\n";
const char *hlsl_preamble_fs =
"#define vec2 float2\n"
"#define vec3 float3\n"
"#define vec4 float4\n"
"#define uvec3 uint3\n"
"#define uvec4 uint4\n"
"#define ivec3 int3\n"
"#define ivec4 int4\n"
"#define mat4 float4x4\n"
"#define mat3x4 float4x3\n" // note how the conventions are backwards
"#define splat3(x) float3(x, x, x)\n"
"#define mix lerp\n"
"#define lowp\n"
"#define mediump\n"
"#define highp\n"
"#define mod(x, y) fmod(x, y)\n";
const char *hlsl_d3d11_preamble_fs =
"#define DISCARD discard\n"
"#define DISCARD_BELOW(x) clip(x);\n";
const char *hlsl_d3d9_preamble_fs =
"#define DISCARD clip(-1)\n"
"#define DISCARD_BELOW(x) clip(x)\n";
const char *vulkan_glsl_preamble_vs =
"#version 450\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"#extension GL_ARB_shading_language_420pack : enable\n"
"#define mul(x, y) ((x) * (y))\n"
"#define splat3(x) vec3(x)\n"
"precision highp float;\n"
"\n";
const char *hlsl_preamble_vs =
"#define vec2 float2\n"
"#define vec3 float3\n"
"#define vec4 float4\n"
"#define ivec2 int2\n"
"#define ivec4 int4\n"
"#define mat4 float4x4\n"
"#define mat3x4 float4x3\n" // note how the conventions are backwards
"#define splat3(x) vec3(x, x, x)\n"
"#define lowp\n"
"#define mediump\n"
"#define highp\n"
"\n";
// Unsafe. But doesn't matter, we'll use big buffers for shader gen.
ShaderWriter & ShaderWriter::F(const char *format, ...) {
va_list args;
va_start(args, format);
p_ += vsprintf(p_, format, args);
va_end(args);
return *this;
}
void ShaderWriter::Preamble(const char **gl_extensions, size_t num_gl_extensions) {
switch (lang_.shaderLanguage) {
case GLSL_VULKAN:
switch (stage_) {
case ShaderStage::Vertex:
W(vulkan_glsl_preamble_vs);
break;
case ShaderStage::Fragment:
W(vulkan_glsl_preamble_fs);
break;
}
break;
case HLSL_D3D11:
case HLSL_D3D9:
switch (stage_) {
case ShaderStage::Vertex:
W(hlsl_preamble_vs);
break;
case ShaderStage::Fragment:
W(hlsl_preamble_fs);
if (lang_.shaderLanguage == HLSL_D3D9) {
W(hlsl_d3d9_preamble_fs);
} else {
W(hlsl_d3d11_preamble_fs);
}
break;
}
break;
default: // OpenGL
F("#version %d%s\n", lang_.glslVersionNumber, lang_.gles && lang_.glslES30 ? " es" : "");
// IMPORTANT! Extensions must be the first thing after #version.
for (size_t i = 0; i < num_gl_extensions; i++) {
F("%s\n", gl_extensions[i]);
}
// Print some system info - useful to gather information directly from screenshots.
F("// %s\n", lang_.driverInfo);
switch (stage_) {
case ShaderStage::Fragment:
C("#define DISCARD discard\n");
if (lang_.gles) {
C("precision lowp float;\n");
if (lang_.glslES30) {
C("precision highp int;\n");
}
}
break;
case ShaderStage::Vertex:
if (lang_.gles) {
C("precision highp float;\n");
}
C("#define gl_VertexIndex gl_VertexID\n");
break;
}
if (!lang_.gles) {
C("#define lowp\n");
C("#define mediump\n");
C("#define highp\n");
}
C("#define splat3(x) vec3(x)\n");
C("#define mul(x, y) ((x) * (y))\n");
break;
}
}
void ShaderWriter::BeginVSMain(Slice<InputDef> inputs, Slice<UniformDef> uniforms, Slice<VaryingDef> varyings) {
_assert_(this->stage_ == ShaderStage::Vertex);
switch (lang_.shaderLanguage) {
case HLSL_D3D11:
case HLSL_D3D9:
{
C("struct VS_OUTPUT {\n");
for (auto &varying : varyings) {
F(" %s %s : %s;\n", varying.type, varying.name, varying.semantic);
}
F(" vec4 pos : %s;\n", lang_.shaderLanguage == HLSL_D3D11 ? "SV_Position" : "POSITION");
C("};\n");
C("VS_OUTPUT main( "); // 2 spaces for the D3D9 rewind
if (lang_.shaderLanguage == HLSL_D3D11) {
C("uint gl_VertexIndex : SV_VertexID, ");
}
Rewind(2); // Get rid of the last comma.
C(") {\n");
C(" vec4 gl_Position;\n");
for (auto &varying : varyings) {
F(" %s %s;\n", varying.type, varying.name);
}
break;
}
case GLSL_VULKAN:
for (auto &varying : varyings) {
F("layout(location = %d) %s out %s %s; // %s\n",
varying.index, varying.precision ? varying.precision : "", varying.type, varying.name, varying.semantic);
}
C("void main() {\n");
break;
default: // OpenGL
for (auto &varying : varyings) {
F("%s %s %s %s; // %s (%d)\n", lang_.varying_vs, varying.precision ? varying.precision : "", varying.type, varying.name, varying.semantic, varying.index);
}
C("void main() {\n");
break;
}
}
void ShaderWriter::BeginFSMain(Slice<UniformDef> uniforms, Slice<VaryingDef> varyings) {
_assert_(this->stage_ == ShaderStage::Fragment);
switch (lang_.shaderLanguage) {
case HLSL_D3D11:
if (!uniforms.is_empty()) {
for (auto &uniform : uniforms) {
//F(" %s %s : %s;\n", uniform.type, uniform.name, uniform.index);
}
}
// Let's do the varyings as parameters to main, no struct.
C("vec4 main(");
for (auto &varying : varyings) {
F(" %s %s : %s, ", varying.type, varying.name, varying.semantic);
}
// Erase the last comma
Rewind(2);
F(") : SV_Target0 {\n");
break;
case HLSL_D3D9:
for (auto &uniform : uniforms) {
F(" %s %s : %s;\n", uniform.type, uniform.name, uniform.index);
}
// Let's do the varyings as parameters to main, no struct.
C("vec4 main(");
for (auto &varying : varyings) {
F(" %s %s : %s, ", varying.type, varying.name, varying.semantic);
}
// Erase the last comma
Rewind(2);
F(") : COLOR {\n");
break;
case GLSL_VULKAN:
for (auto &varying : varyings) {
F("layout(location = %d) %s in %s %s; // %s\n", varying.index, varying.precision ? varying.precision : "", varying.type, varying.name, varying.semantic);
}
C("layout(location = 0, index = 0) out vec4 fragColor0;\n");
C("\nvoid main() {\n");
break;
default:
for (auto &varying : varyings) {
F("%s %s %s %s; // %s\n", lang_.varying_fs, varying.precision ? varying.precision : "", varying.type, varying.name, varying.semantic);
}
if (!strcmp(lang_.fragColor0, "fragColor0")) {
C("out vec4 fragColor0;\n");
}
C("\nvoid main() {\n");
break;
}
}
void ShaderWriter::EndVSMain(Slice<VaryingDef> varyings) {
_assert_(this->stage_ == ShaderStage::Vertex);
switch (lang_.shaderLanguage) {
case HLSL_D3D11:
case HLSL_D3D9:
C(" VS_OUTPUT vs_out;\n");
C(" vs_out.pos = gl_Position;\n");
for (auto &varying : varyings) {
F(" vs_out.%s = %s;\n", varying.name, varying.name);
}
C(" return vs_out;\n");
break;
case GLSL_VULKAN:
default: // OpenGL
break;
}
C("}\n");
}
void ShaderWriter::EndFSMain(const char *vec4_color_variable) {
_assert_(this->stage_ == ShaderStage::Fragment);
switch (lang_.shaderLanguage) {
case HLSL_D3D11:
case HLSL_D3D9:
F(" return %s;\n", vec4_color_variable);
break;
case GLSL_VULKAN:
default: // OpenGL
F(" %s = %s;\n", lang_.fragColor0, vec4_color_variable);
break;
}
C("}\n");
}
void ShaderWriter::HighPrecisionFloat() {
if ((ShaderLanguageIsOpenGL(lang_.shaderLanguage) && lang_.gles) || lang_.shaderLanguage == GLSL_VULKAN) {
C("precision highp float;\n");
}
}
void ShaderWriter::DeclareTexture2D(const char *name, int binding) {
switch (lang_.shaderLanguage) {
case HLSL_D3D11:
F("Texture2D<float4> %s : register(t%d);\n", name, binding);
break;
case HLSL_D3D9:
break;
case GLSL_VULKAN:
// In the thin3d descriptor set layout, textures start at 1 in set 0. Hence the +1.
F("layout(set = 0, binding = %d) uniform sampler2D %s;\n", binding + 1, name);
break;
default:
F("uniform sampler2D %s;\n", name);
break;
}
}
void ShaderWriter::DeclareSampler2D(const char *name, int binding) {
// We only use separate samplers in HLSL D3D11, where we have no choice.
switch (lang_.shaderLanguage) {
case HLSL_D3D11:
F("SamplerState %s : register(s%d);\n", name, binding);
break;
}
}
ShaderWriter &ShaderWriter::SampleTexture2D(const char *texName, const char *samplerName, const char *uv) {
switch (lang_.shaderLanguage) {
case HLSL_D3D11:
F("%s.Sample(%s, %s)", texName, samplerName, uv);
break;
case HLSL_D3D9:
F("tex2D(%s, %s)", texName, uv);
break;
default:
// Note: we ignore the sampler. make sure you bound samplers to the textures correctly.
F("%s(%s, %s)", lang_.texture, texName, uv);
break;
}
return *this;
}
+101
View File
@@ -0,0 +1,101 @@
#pragma once
#include <cstring>
#include "Common/Log.h"
#include "Common/GPU/Shader.h"
#include "GPU/ge_constants.h"
#include "GPU/GPUCommon.h"
#include "Common/Data/Collections/Slice.h"
#include "Common/GPU/thin3d.h"
// Helps generate a shader compatible with all backends.
//
// Can use the uniform buffer support in thin3d.
//
// Using #defines and magic in this class, we partially define our own shader language that basically looks
// like GLSL, but has a few little oddities like splat3.
struct InputDef {
const char *type;
const char *name;
};
struct UniformDef {
const char *type;
const char *name;
int index;
};
struct VaryingDef {
const char *type;
const char *name;
const char *semantic;
int index;
const char *precision;
};
class ShaderWriter {
public:
ShaderWriter(char *buffer, const ShaderLanguageDesc &lang, ShaderStage stage, const char **gl_extensions, size_t num_gl_extensions) : p_(buffer), lang_(lang), stage_(stage) {
Preamble(gl_extensions, num_gl_extensions);
}
// I tried to call all three write functions "W", but only MSVC
// managed to disentangle the ambiguities, so had to give up on that.
// Assumes the input is zero-terminated.
// C : Copies a buffer directly to the stream.
template<size_t T>
ShaderWriter &C(const char(&text)[T]) {
memcpy(p_, text, T);
p_ += T - 1;
return *this;
}
// W: Writes a zero-terminated string to the stream.
ShaderWriter &W(const char *text) {
size_t len = strlen(text);
memcpy(p_, text, len + 1);
p_ += len;
return *this;
}
// F: Formats into the buffer.
ShaderWriter &F(const char *format, ...);
// Useful for fragment shaders in GLES.
// We always default integers to high precision.
void HighPrecisionFloat();
// Several of the shader languages ignore samplers, beware of that.
void DeclareSampler2D(const char *name, int binding);
void DeclareTexture2D(const char *name, int binding);
ShaderWriter &SampleTexture2D(const char *texName, const char *samplerName, const char *uv);
// Simple shaders with no special tricks.
void BeginVSMain(Slice<InputDef> inputs, Slice<UniformDef> uniforms, Slice<VaryingDef> varyings);
void BeginFSMain(Slice<UniformDef> uniforms, Slice<VaryingDef> varyings);
// For simple shaders that output a single color, we can deal with this generically.
void EndVSMain(Slice<VaryingDef> varyings);
void EndFSMain(const char *vec4_color_variable);
void Rewind(size_t offset) {
p_ -= offset;
}
// Can probably remove this
char *GetPos() {
return p_;
}
private:
void Preamble(const char **gl_extensions, size_t num_gl_extensions);
char *p_;
const ShaderLanguageDesc &lang_;
const ShaderStage stage_;
};
+43 -29
View File
@@ -146,10 +146,14 @@ VkResult VulkanContext::CreateInstance(const CreateInfo &info) {
}
}
// Temporary hack for libretro. For some reason, when we try to load the functions from this extension,
// we get null pointers when running libretro. Quite strange.
#if !defined(__LIBRETRO__)
if (IsInstanceExtensionAvailable(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
instance_extensions_enabled_.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
extensionsLookup_.KHR_get_physical_device_properties2 = true;
}
#endif
// Validate that all the instance extensions we ask for are actually available.
for (auto ext : instance_extensions_enabled_) {
@@ -537,9 +541,13 @@ void VulkanContext::ChooseDevice(int physical_device) {
break;
}
}
if (deviceInfo_.preferredDepthStencilFormat == VK_FORMAT_UNDEFINED) {
// WTF? This is bad.
ERROR_LOG(G3D, "Could not find a usable depth stencil format.");
_assert_msg_(deviceInfo_.preferredDepthStencilFormat != VK_FORMAT_UNDEFINED, "Could not find a usable depth stencil format.");
VkFormatProperties preferredProps;
vkGetPhysicalDeviceFormatProperties(physical_devices_[physical_device_], deviceInfo_.preferredDepthStencilFormat, &preferredProps);
if ((preferredProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) &&
(preferredProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT)) {
deviceInfo_.canBlitToPreferredDepthStencilFormat = true;
}
// This is as good a place as any to do this.
@@ -915,6 +923,11 @@ static std::string surface_transforms_to_string(VkSurfaceTransformFlagsKHR trans
bool VulkanContext::InitSwapchain() {
VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_devices_[physical_device_], surface_, &surfCapabilities_);
if (res == VK_ERROR_SURFACE_LOST_KHR) {
// Not much to do.
ERROR_LOG(G3D, "VK: Surface lost in InitSwapchain");
return false;
}
_dbg_assert_(res == VK_SUCCESS);
uint32_t presentModeCount;
res = vkGetPhysicalDeviceSurfacePresentModesKHR(physical_devices_[physical_device_], surface_, &presentModeCount, nullptr);
@@ -1122,24 +1135,6 @@ void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, int baseMip, int
VkImageLayout oldImageLayout, VkImageLayout newImageLayout,
VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask) {
#ifdef VULKAN_USE_GENERAL_LAYOUT_FOR_COLOR
if (aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) {
// Hack to disable transaction elimination on ARM Mali.
if (oldImageLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL || oldImageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
oldImageLayout = VK_IMAGE_LAYOUT_GENERAL;
if (newImageLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL || newImageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
newImageLayout = VK_IMAGE_LAYOUT_GENERAL;
}
#endif
#ifdef VULKAN_USE_GENERAL_LAYOUT_FOR_DEPTH_STENCIL
if (aspectMask != VK_IMAGE_ASPECT_COLOR_BIT) {
// Hack to disable transaction elimination on ARM Mali.
if (oldImageLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL || oldImageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
oldImageLayout = VK_IMAGE_LAYOUT_GENERAL;
if (newImageLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL || newImageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
newImageLayout = VK_IMAGE_LAYOUT_GENERAL;
}
#endif
VkImageMemoryBarrier image_memory_barrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
image_memory_barrier.srcAccessMask = srcAccessMask;
image_memory_barrier.dstAccessMask = dstAccessMask;
@@ -1182,24 +1177,43 @@ EShLanguage FindLanguage(const VkShaderStageFlagBits shader_type) {
// Compile a given string containing GLSL into SPV for use by VK
// Return value of false means an error was encountered.
bool GLSLtoSPV(const VkShaderStageFlagBits shader_type,
const char *pshader,
std::vector<unsigned int> &spirv, std::string *errorMessage) {
bool GLSLtoSPV(const VkShaderStageFlagBits shader_type, const char *sourceCode, GLSLVariant variant,
std::vector<unsigned int> &spirv, std::string *errorMessage) {
glslang::TProgram program;
const char *shaderStrings[1];
EProfile profile = ECoreProfile;
int defaultVersion = 450;
TBuiltInResource Resources;
init_resources(Resources);
// Enable SPIR-V and Vulkan rules when parsing GLSL
EShMessages messages = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules);
int defaultVersion;
EShMessages messages;
EProfile profile;
switch (variant) {
case GLSLVariant::VULKAN:
// Enable SPIR-V and Vulkan rules when parsing GLSL
messages = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules);
defaultVersion = 450;
profile = ECoreProfile;
break;
case GLSLVariant::GL140:
messages = (EShMessages)(EShMsgDefault);
defaultVersion = 140;
profile = ECompatibilityProfile;
break;
case GLSLVariant::GLES300:
messages = (EShMessages)(EShMsgDefault);
defaultVersion = 300;
profile = EEsProfile;
break;
default:
return false;
}
EShLanguage stage = FindLanguage(shader_type);
glslang::TShader shader(stage);
shaderStrings[0] = pshader;
shaderStrings[0] = sourceCode;
shader.setStrings(shaderStrings, 1);
if (!shader.parse(&Resources, defaultVersion, profile, false, true, messages)) {
+9 -7
View File
@@ -52,6 +52,7 @@ enum WindowSystem {
struct VulkanPhysicalDeviceInfo {
VkFormat preferredDepthStencilFormat;
bool canBlitToPreferredDepthStencilFormat;
};
// This is a bit repetitive...
@@ -106,12 +107,6 @@ private:
std::vector<Callback> callbacks_;
};
// Useful for debugging on ARM Mali. This eliminates transaction elimination
// which can cause artifacts if you get barriers wrong (or if there are driver bugs).
// Cost is reduced performance on some GPU architectures.
// #define VULKAN_USE_GENERAL_LAYOUT_FOR_COLOR
// #define VULKAN_USE_GENERAL_LAYOUT_FOR_DEPTH_STENCIL
// VulkanContext manages the device and swapchain, and deferred deletion of objects.
class VulkanContext {
public:
@@ -372,7 +367,14 @@ void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, int baseMip, int
// GLSL compiler
void init_glslang();
void finalize_glslang();
bool GLSLtoSPV(const VkShaderStageFlagBits shader_type, const char *pshader, std::vector<uint32_t> &spirv, std::string *errorMessage = nullptr);
enum class GLSLVariant {
VULKAN,
GL140,
GLES300,
};
bool GLSLtoSPV(const VkShaderStageFlagBits shader_type, const char *sourceCode, GLSLVariant variant, std::vector<uint32_t> &spirv, std::string *errorMessage);
const char *VulkanResultToString(VkResult res);
std::string FormatDriverVersion(const VkPhysicalDeviceProperties &props);
+220 -199
View File
@@ -12,8 +12,7 @@ void VulkanQueueRunner::CreateDeviceObjects() {
INFO_LOG(G3D, "VulkanQueueRunner::CreateDeviceObjects");
InitBackbufferRenderPass();
framebufferRenderPass_ = GetRenderPass(VKRRenderPassAction::CLEAR, VKRRenderPassAction::CLEAR, VKRRenderPassAction::CLEAR,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
framebufferRenderPass_ = GetRenderPass(VKRRenderPassAction::CLEAR, VKRRenderPassAction::CLEAR, VKRRenderPassAction::CLEAR);
#if 0
// Just to check whether it makes sense to split some of these. drawidx is way bigger than the others...
@@ -126,22 +125,13 @@ void VulkanQueueRunner::InitBackbufferRenderPass() {
attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; // Don't care about storing backbuffer Z - we clear it anyway.
attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
#ifdef VULKAN_USE_GENERAL_LAYOUT_FOR_DEPTH_STENCIL
attachments[1].initialLayout = VK_IMAGE_LAYOUT_GENERAL;
attachments[1].finalLayout = VK_IMAGE_LAYOUT_GENERAL;
#else
attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
#endif
attachments[1].flags = 0;
VkAttachmentReference color_reference{};
color_reference.attachment = 0;
#ifdef VULKAN_USE_GENERAL_LAYOUT_FOR_COLOR
color_reference.layout = VK_IMAGE_LAYOUT_GENERAL;
#else
color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
#endif
VkAttachmentReference depth_reference{};
depth_reference.attachment = 1;
@@ -204,13 +194,8 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(const RPKey &key) {
attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
#ifdef VULKAN_USE_GENERAL_LAYOUT_FOR_COLOR
attachments[0].initialLayout = VK_IMAGE_LAYOUT_GENERAL;
attachments[0].finalLayout = VK_IMAGE_LAYOUT_GENERAL;
#else
attachments[0].initialLayout = key.prevColorLayout;
attachments[0].finalLayout = key.finalColorLayout;
#endif
attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments[0].flags = 0;
attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat;
@@ -239,13 +224,8 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(const RPKey &key) {
}
attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
#ifdef VULKAN_USE_GENERAL_LAYOUT_FOR_DEPTH_STENCIL
attachments[1].initialLayout = VK_IMAGE_LAYOUT_GENERAL;
attachments[1].finalLayout = VK_IMAGE_LAYOUT_GENERAL;
#else
attachments[1].initialLayout = key.prevDepthStencilLayout;
attachments[1].finalLayout = key.finalDepthStencilLayout;
#endif
attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments[1].flags = 0;
VkAttachmentReference color_reference{};
@@ -268,136 +248,12 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(const RPKey &key) {
subpass.preserveAttachmentCount = 0;
subpass.pPreserveAttachments = nullptr;
VkSubpassDependency deps[2]{};
int numDeps = 0;
switch (key.prevColorLayout) {
case VK_IMAGE_LAYOUT_UNDEFINED:
// No need to specify stage or access.
break;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
// Already the right color layout. Unclear that we need to do a lot here..
break;
case VK_IMAGE_LAYOUT_GENERAL:
// We came from the Mali workaround, and are transitioning back to COLOR_ATTACHMENT_OPTIMAL.
deps[numDeps].srcAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
deps[numDeps].srcStageMask |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
break;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
deps[numDeps].srcAccessMask |= VK_ACCESS_SHADER_READ_BIT;
deps[numDeps].srcStageMask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
deps[numDeps].srcAccessMask |= VK_ACCESS_TRANSFER_WRITE_BIT;
deps[numDeps].srcStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
deps[numDeps].srcAccessMask |= VK_ACCESS_TRANSFER_READ_BIT;
deps[numDeps].srcStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
default:
_dbg_assert_msg_(false, "GetRenderPass: Unexpected color layout %d", (int)key.prevColorLayout);
break;
}
switch (key.prevDepthStencilLayout) {
case VK_IMAGE_LAYOUT_UNDEFINED:
// No need to specify stage or access.
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
// Already the right depth layout. Unclear that we need to do a lot here..
break;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
deps[numDeps].srcAccessMask |= VK_ACCESS_SHADER_READ_BIT;
deps[numDeps].srcStageMask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
deps[numDeps].srcAccessMask |= VK_ACCESS_TRANSFER_READ_BIT;
deps[numDeps].srcStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
deps[numDeps].srcAccessMask |= VK_ACCESS_TRANSFER_WRITE_BIT;
deps[numDeps].srcStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
default:
_dbg_assert_msg_(false, "GetRenderPass: Unexpected depth layout %d", (int)key.prevDepthStencilLayout);
break;
}
if (deps[numDeps].srcAccessMask) {
deps[numDeps].srcSubpass = VK_SUBPASS_EXTERNAL;
deps[numDeps].dstSubpass = 0;
deps[numDeps].dependencyFlags = 0;
deps[numDeps].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
deps[numDeps].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
numDeps++;
}
// And the final transition.
// Don't need to transition it if VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL.
switch (key.finalColorLayout) {
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
deps[numDeps].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
deps[numDeps].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
deps[numDeps].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
deps[numDeps].dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
deps[numDeps].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
deps[numDeps].dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_UNDEFINED:
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
// Nothing to do.
break;
default:
_dbg_assert_msg_(false, "GetRenderPass: Unexpected final color layout %d", (int)key.finalColorLayout);
break;
}
switch (key.finalDepthStencilLayout) {
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
deps[numDeps].dstAccessMask |= VK_ACCESS_SHADER_READ_BIT;
deps[numDeps].dstStageMask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
deps[numDeps].dstAccessMask |= VK_ACCESS_TRANSFER_READ_BIT;
deps[numDeps].dstStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
deps[numDeps].dstAccessMask |= VK_ACCESS_TRANSFER_READ_BIT;
deps[numDeps].dstStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_UNDEFINED:
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
// Nothing to do.
break;
default:
_dbg_assert_msg_(false, "GetRenderPass: Unexpected final depth layout %d", (int)key.finalDepthStencilLayout);
break;
}
if (deps[numDeps].dstAccessMask) {
deps[numDeps].srcSubpass = 0;
deps[numDeps].dstSubpass = VK_SUBPASS_EXTERNAL;
deps[numDeps].dependencyFlags = 0;
deps[numDeps].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
deps[numDeps].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
numDeps++;
}
VkRenderPassCreateInfo rp{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
rp.attachmentCount = 2;
rp.pAttachments = attachments;
rp.subpassCount = 1;
rp.pSubpasses = &subpass;
if (numDeps) {
rp.dependencyCount = numDeps;
rp.pDependencies = deps;
}
VkResult res = vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &pass);
_assert_(res == VK_SUCCESS);
_assert_(pass != VK_NULL_HANDLE);
@@ -777,7 +633,9 @@ std::string VulkanQueueRunner::StepToString(const VKRStep &step) const {
{
int w = step.render.framebuffer ? step.render.framebuffer->width : vulkan_->GetBackbufferWidth();
int h = step.render.framebuffer ? step.render.framebuffer->height : vulkan_->GetBackbufferHeight();
snprintf(buffer, sizeof(buffer), "RENDER %s (draws: %d, %dx%d, fb: %p, )", step.tag, step.render.numDraws, w, h, step.render.framebuffer);
int actual_w = step.render.renderArea.extent.width;
int actual_h = step.render.renderArea.extent.height;
snprintf(buffer, sizeof(buffer), "RENDER %s (draws: %d, %dx%d/%dx%d, fb: %p, )", step.tag, step.render.numDraws, actual_w, actual_h, w, h, step.render.framebuffer);
break;
}
case VKRStepType::COPY:
@@ -1016,6 +874,194 @@ void VulkanQueueRunner::LogReadbackImage(const VKRStep &step) {
INFO_LOG(G3D, "%s", StepToString(step).c_str());
}
void TransitionToOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayout colorLayout, VkImage depthStencilImage, VkImageLayout depthStencilLayout) {
VkPipelineStageFlags srcStageMask = 0;
VkPipelineStageFlags dstStageMask = 0;
int barrierCount = 0;
VkImageMemoryBarrier barrier[2]{};
if (colorLayout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier[0].pNext = nullptr;
switch (colorLayout) {
case VK_IMAGE_LAYOUT_UNDEFINED:
// No need to specify stage or access.
break;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
// Already the right color layout. Unclear that we need to do a lot here..
break;
case VK_IMAGE_LAYOUT_GENERAL:
// We came from the Mali workaround, and are transitioning back to COLOR_ATTACHMENT_OPTIMAL.
barrier[0].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
srcStageMask |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
break;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
barrier[0].srcAccessMask |= VK_ACCESS_SHADER_READ_BIT;
srcStageMask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
barrier[0].srcAccessMask |= VK_ACCESS_TRANSFER_WRITE_BIT;
srcStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
barrier[0].srcAccessMask |= VK_ACCESS_TRANSFER_READ_BIT;
srcStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
default:
_dbg_assert_msg_(false, "GetRenderPass: Unexpected color layout %d", (int)colorLayout);
break;
}
dstStageMask |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
barrier[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
barrier[0].oldLayout = colorLayout;
barrier[0].newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
barrier[0].image = colorImage;
barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier[0].subresourceRange.baseMipLevel = 0;
barrier[0].subresourceRange.levelCount = 1;
barrier[0].subresourceRange.layerCount = 1;
barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrierCount++;
}
if (depthStencilImage != VK_NULL_HANDLE && depthStencilLayout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
barrier[barrierCount].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier[barrierCount].pNext = nullptr;
switch (depthStencilLayout) {
case VK_IMAGE_LAYOUT_UNDEFINED:
// No need to specify stage or access.
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
// Already the right depth layout. Unclear that we need to do a lot here..
break;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
barrier[barrierCount].srcAccessMask |= VK_ACCESS_SHADER_READ_BIT;
srcStageMask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
barrier[barrierCount].srcAccessMask |= VK_ACCESS_TRANSFER_READ_BIT;
srcStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
barrier[barrierCount].srcAccessMask |= VK_ACCESS_TRANSFER_WRITE_BIT;
srcStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
default:
_dbg_assert_msg_(false, "GetRenderPass: Unexpected depth layout %d", (int)depthStencilLayout);
break;
}
dstStageMask |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
barrier[barrierCount].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
barrier[barrierCount].oldLayout = depthStencilLayout;
barrier[barrierCount].newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
barrier[barrierCount].image = depthStencilImage;
barrier[barrierCount].subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
barrier[barrierCount].subresourceRange.baseMipLevel = 0;
barrier[barrierCount].subresourceRange.levelCount = 1;
barrier[barrierCount].subresourceRange.layerCount = 1;
barrier[barrierCount].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier[barrierCount].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrierCount++;
}
if (barrierCount) {
vkCmdPipelineBarrier(cmd, srcStageMask, dstStageMask, VK_DEPENDENCY_BY_REGION_BIT, 0, nullptr, 0, nullptr, barrierCount, barrier);
}
}
void TransitionFromOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayout colorLayout, VkImage depthStencilImage, VkImageLayout depthStencilLayout) {
VkPipelineStageFlags srcStageMask = 0;
VkPipelineStageFlags dstStageMask = 0;
// If layouts aren't optimal, transition them.
VkImageMemoryBarrier barrier[2]{};
int barrierCount = 0;
if (colorLayout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier[0].pNext = nullptr;
srcStageMask |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
barrier[0].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
// And the final transition.
// Don't need to transition it if VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL.
switch (colorLayout) {
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
barrier[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
dstStageMask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
dstStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
dstStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_UNDEFINED:
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
// Nothing to do.
break;
default:
_dbg_assert_msg_(false, "GetRenderPass: Unexpected final color layout %d", (int)colorLayout);
break;
}
barrier[0].oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
barrier[0].newLayout = colorLayout;
barrier[0].image = colorImage;
barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier[0].subresourceRange.baseMipLevel = 0;
barrier[0].subresourceRange.levelCount = 1;
barrier[0].subresourceRange.layerCount = 1;
barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrierCount++;
}
if (depthStencilImage && depthStencilLayout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
barrier[barrierCount].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier[barrierCount].pNext = nullptr;
srcStageMask |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
barrier[barrierCount].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
switch (depthStencilLayout) {
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
barrier[barrierCount].dstAccessMask |= VK_ACCESS_SHADER_READ_BIT;
dstStageMask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
barrier[barrierCount].dstAccessMask |= VK_ACCESS_TRANSFER_READ_BIT;
dstStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
barrier[barrierCount].dstAccessMask |= VK_ACCESS_TRANSFER_READ_BIT;
dstStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_UNDEFINED:
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
// Nothing to do.
break;
default:
_dbg_assert_msg_(false, "GetRenderPass: Unexpected final depth layout %d", (int)depthStencilLayout);
break;
}
barrier[barrierCount].oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
barrier[barrierCount].newLayout = depthStencilLayout;
barrier[barrierCount].image = depthStencilImage;
barrier[barrierCount].subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
barrier[barrierCount].subresourceRange.baseMipLevel = 0;
barrier[barrierCount].subresourceRange.levelCount = 1;
barrier[barrierCount].subresourceRange.layerCount = 1;
barrier[barrierCount].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier[barrierCount].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrierCount++;
}
if (barrierCount) {
vkCmdPipelineBarrier(cmd, srcStageMask, dstStageMask, VK_DEPENDENCY_BY_REGION_BIT, 0, nullptr, 0, nullptr, barrierCount, barrier);
}
}
void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd) {
// TODO: If there are multiple, we can transition them together.
@@ -1311,8 +1357,10 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
}
vkCmdEndRenderPass(cmd);
// The renderpass handles the layout transition.
if (fb) {
// If the desired final layout aren't the optimal layout for rendering, transition.
TransitionFromOptimal(cmd, fb->color.image, step.render.finalColorLayout, fb->depth.image, step.render.finalDepthStencilLayout);
fb->color.layout = step.render.finalColorLayout;
fb->depth.layout = step.render.finalDepthStencilLayout;
}
@@ -1350,15 +1398,12 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step
fb->color.layout = VK_IMAGE_LAYOUT_GENERAL;
}
renderPass = GetRenderPass(
step.render.color, step.render.depth, step.render.stencil,
fb->color.layout, fb->depth.layout,
step.render.finalColorLayout,
step.render.finalDepthStencilLayout);
TransitionToOptimal(cmd, fb->color.image, fb->color.layout, fb->depth.image, fb->depth.layout);
// We now do any layout pretransitions as part of the render pass.
fb->color.layout = step.render.finalColorLayout;
fb->depth.layout = step.render.finalDepthStencilLayout;
renderPass = GetRenderPass(step.render.color, step.render.depth, step.render.stencil);
// The transition from the optimal format happens after EndRenderPass, now that we don't
// do it as part of the renderpass itself anymore.
if (step.render.color == VKRRenderPassAction::CLEAR) {
Uint8x4ToFloat4(clearVal[0].color.float32, step.render.clearColor);
@@ -1371,6 +1416,8 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step
}
} else {
framebuf = backbuffer_;
// Raw, rotated backbuffer size.
w = vulkan_->GetBackbufferWidth();
h = vulkan_->GetBackbufferHeight();
renderPass = GetBackbufferRenderPass();
@@ -1383,10 +1430,20 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step
VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
rp_begin.renderPass = renderPass;
rp_begin.framebuffer = framebuf;
rp_begin.renderArea.offset.x = 0;
rp_begin.renderArea.offset.y = 0;
rp_begin.renderArea.extent.width = w;
rp_begin.renderArea.extent.height = h;
VkRect2D rc = step.render.renderArea;
if (!step.render.framebuffer) {
// Rendering to backbuffer, must rotate, just like scissors.
DisplayRect<int> rotated_rc{ rc.offset.x, rc.offset.y, (int)rc.extent.width, (int)rc.extent.height };
RotateRectToDisplay(rotated_rc, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight());
rc.offset.x = rotated_rc.x;
rc.offset.y = rotated_rc.y;
rc.extent.width = rotated_rc.w;
rc.extent.height = rotated_rc.h;
}
rp_begin.renderArea = rc;
rp_begin.clearValueCount = numClearVals;
rp_begin.pClearValues = numClearVals ? clearVal : nullptr;
vkCmdBeginRenderPass(cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
@@ -1580,24 +1637,6 @@ void VulkanQueueRunner::SetupTransitionToTransferSrc(VKRImage &img, VkImageMemor
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
img.layout = barrier.newLayout;
// NOTE: Must do this AFTER updating img.layout to avoid behaviour differences.
#ifdef VULKAN_USE_GENERAL_LAYOUT_FOR_COLOR
if (aspect == VK_IMAGE_ASPECT_COLOR_BIT) {
if (barrier.oldLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL || barrier.oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
if (barrier.newLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL || barrier.newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
}
#endif
#ifdef VULKAN_USE_GENERAL_LAYOUT_FOR_DEPTH_STENCIL
if (aspect != VK_IMAGE_ASPECT_COLOR_BIT) {
if (barrier.oldLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL || barrier.oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
if (barrier.newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL || barrier.newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
}
#endif
}
void VulkanQueueRunner::SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect) {
@@ -1639,24 +1678,6 @@ void VulkanQueueRunner::SetupTransitionToTransferDst(VKRImage &img, VkImageMemor
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
img.layout = barrier.newLayout;
// NOTE: Must do this AFTER updating img.layout to avoid behaviour differences.
#ifdef VULKAN_USE_GENERAL_LAYOUT_FOR_COLOR
if (aspect == VK_IMAGE_ASPECT_COLOR_BIT) {
if (barrier.oldLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL || barrier.oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
if (barrier.newLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL || barrier.newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
}
#endif
#ifdef VULKAN_USE_GENERAL_LAYOUT_FOR_DEPTH_STENCIL
if (aspect != VK_IMAGE_ASPECT_COLOR_BIT) {
if (barrier.oldLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL || barrier.oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
if (barrier.newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL || barrier.newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
}
#endif
}
void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd) {
+13 -7
View File
@@ -32,6 +32,13 @@ enum class VKRRenderCommand : uint8_t {
NUM_RENDER_COMMANDS,
};
enum PipelineFlags {
PIPELINE_FLAG_NONE = 0,
PIPELINE_FLAG_USES_LINES = (1 << 2),
PIPELINE_FLAG_USES_BLEND_CONSTANT = (1 << 3),
PIPELINE_FLAG_USES_DEPTH_STENCIL = (1 << 4), // Reads or writes the depth buffer.
};
struct VkRenderData {
VKRRenderCommand cmd;
union {
@@ -143,6 +150,8 @@ struct VKRStep {
int numReads;
VkImageLayout finalColorLayout;
VkImageLayout finalDepthStencilLayout;
u32 pipelineFlags;
VkRect2D renderArea;
} render;
struct {
VKRFramebuffer *src;
@@ -209,17 +218,12 @@ public:
VKRRenderPassAction colorLoadAction;
VKRRenderPassAction depthLoadAction;
VKRRenderPassAction stencilLoadAction;
VkImageLayout prevColorLayout;
VkImageLayout prevDepthStencilLayout;
VkImageLayout finalColorLayout;
VkImageLayout finalDepthStencilLayout;
};
// Only call this from the render thread! Also ok during initialization (LoadCache).
VkRenderPass GetRenderPass(
VKRRenderPassAction colorLoadAction, VKRRenderPassAction depthLoadAction, VKRRenderPassAction stencilLoadAction,
VkImageLayout prevColorLayout, VkImageLayout prevDepthStencilLayout, VkImageLayout finalColorLayout, VkImageLayout finalDepthStencilLayout) {
RPKey key{ colorLoadAction, depthLoadAction, stencilLoadAction, prevColorLayout, prevDepthStencilLayout, finalColorLayout, finalDepthStencilLayout };
VKRRenderPassAction colorLoadAction, VKRRenderPassAction depthLoadAction, VKRRenderPassAction stencilLoadAction) {
RPKey key{ colorLoadAction, depthLoadAction, stencilLoadAction };
return GetRenderPass(key);
}
@@ -271,6 +275,8 @@ private:
VkImage backbufferImage_ = VK_NULL_HANDLE;
VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE;
// The "Compatible" render pass. Used when creating pipelines that render to "normal" framebuffers.
VkRenderPass framebufferRenderPass_ = VK_NULL_HANDLE;
// Renderpasses, all combinations of preserving or clearing or dont-care-ing fb contents.
+75 -24
View File
@@ -213,7 +213,11 @@ VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan) : vulkan_(vulkan
}
}
void VulkanRenderManager::CreateBackbuffers() {
bool VulkanRenderManager::CreateBackbuffers() {
if (!vulkan_->GetSwapchain()) {
ERROR_LOG(G3D, "No swapchain - can't create backbuffers");
return false;
}
VkResult res = vkGetSwapchainImagesKHR(vulkan_->GetDevice(), vulkan_->GetSwapchain(), &swapchainImageCount_, nullptr);
_dbg_assert_(res == VK_SUCCESS);
@@ -222,7 +226,7 @@ void VulkanRenderManager::CreateBackbuffers() {
if (res != VK_SUCCESS) {
ERROR_LOG(G3D, "vkGetSwapchainImagesKHR failed");
delete[] swapchainImages;
return;
return false;
}
VkCommandBuffer cmdInit = GetInitCmd();
@@ -260,8 +264,8 @@ void VulkanRenderManager::CreateBackbuffers() {
if (InitDepthStencilBuffer(cmdInit)) {
InitBackbufferFramebuffers(vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight());
}
curWidth_ = -1;
curHeight_ = -1;
curWidthRaw_ = -1;
curHeightRaw_ = -1;
if (HasBackbuffers()) {
VLOG("Backbuffers Created");
@@ -283,6 +287,7 @@ void VulkanRenderManager::CreateBackbuffers() {
INFO_LOG(G3D, "Starting Vulkan submission thread (threadInitFrame_ = %d)", vulkan_->GetCurFrame());
thread_ = std::thread(&VulkanRenderManager::ThreadFunc, this);
}
return true;
}
void VulkanRenderManager::StopThread() {
@@ -429,7 +434,6 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling) {
int curFrame = vulkan_->GetCurFrame();
FrameData &frameData = frameData_[curFrame];
frameData.profilingEnabled_ = enableProfiling;
// Make sure the very last command buffer from the frame before the previous has been fully executed.
if (useThread_) {
@@ -442,9 +446,12 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling) {
}
VLOG("PUSH: Fencing %d", curFrame);
vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX);
vkResetFences(device, 1, &frameData.fence);
// Can't set this until after the fence.
frameData.profilingEnabled_ = enableProfiling;
frameData.readbackFenceUsed = false;
uint64_t queryResults[MAX_TIMESTAMP_QUERIES];
@@ -497,6 +504,7 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling) {
if (frameData.profilingEnabled_) {
// For various reasons, we need to always use an init cmd buffer in this case to perform the vkCmdResetQueryPool,
// unless we want to limit ourselves to only measure the main cmd buffer.
// Later versions of Vulkan have support for clearing queries on the CPU timeline, but we don't want to rely on that.
// Reserve the first two queries for initCmd.
frameData.profile.timestampDescriptions.push_back("initCmd Begin");
frameData.profile.timestampDescriptions.push_back("initCmd");
@@ -524,9 +532,29 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() {
return frameData_[curFrame].initCmd;
}
void VulkanRenderManager::EndCurRenderStep() {
// Save the accumulated pipeline flags so we can use that to configure the render pass.
// We'll often be able to avoid loading/saving the depth/stencil buffer.
if (curRenderStep_) {
curRenderStep_->render.pipelineFlags = curPipelineFlags_;
// We don't do this optimization for very small targets, probably not worth it.
if (!curRenderArea_.Empty() && (curWidth_ > 32 && curHeight_ > 32)) {
curRenderStep_->render.renderArea = curRenderArea_.ToVkRect2D();
} else {
curRenderStep_->render.renderArea.offset = {};
curRenderStep_->render.renderArea.extent = { (uint32_t)curWidth_, (uint32_t)curHeight_ };
}
curRenderArea_.Reset();
// We no longer have a current render step.
curRenderStep_ = nullptr;
curPipelineFlags_ = 0;
}
}
void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, VKRRenderPassAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil, const char *tag) {
_dbg_assert_(insideFrame_);
// Eliminate dupes, instantly convert to a clear if possible.
// Eliminate dupes (bind of the framebuffer we already are rendering to), instantly convert to a clear if possible.
if (!steps_.empty() && steps_.back()->stepType == VKRStepType::RENDER && steps_.back()->render.framebuffer == fb) {
u32 clearMask = 0;
if (color == VKRRenderPassAction::CLEAR) {
@@ -562,21 +590,26 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR
data.clear.clearStencil = clearStencil;
data.clear.clearMask = clearMask;
curRenderStep_->commands.push_back(data);
curRenderArea_.SetRect(0, 0, curWidth_, curHeight_);
}
return;
}
}
// More redundant bind elimination.
if (curRenderStep_ && curRenderStep_->commands.size() == 0 && curRenderStep_->render.color != VKRRenderPassAction::CLEAR && curRenderStep_->render.depth != VKRRenderPassAction::CLEAR && curRenderStep_->render.stencil != VKRRenderPassAction::CLEAR) {
// Can trivially kill the last empty render step.
_dbg_assert_(steps_.back() == curRenderStep_);
delete steps_.back();
steps_.pop_back();
curRenderStep_ = nullptr;
}
if (curRenderStep_ && curRenderStep_->commands.size() == 0) {
VLOG("Empty render step. Usually happens after uploading pixels..");
if (curRenderStep_) {
if (curRenderStep_->commands.empty()) {
if (curRenderStep_->render.color != VKRRenderPassAction::CLEAR && curRenderStep_->render.depth != VKRRenderPassAction::CLEAR && curRenderStep_->render.stencil != VKRRenderPassAction::CLEAR) {
// Can trivially kill the last empty render step.
_dbg_assert_(steps_.back() == curRenderStep_);
delete steps_.back();
steps_.pop_back();
curRenderStep_ = nullptr;
}
VLOG("Empty render step. Usually happens after uploading pixels..");
}
EndCurRenderStep();
}
// Older Mali drivers have issues with depth and stencil don't match load/clear/etc.
@@ -622,11 +655,24 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR
curStepHasViewport_ = false;
curStepHasScissor_ = false;
if (fb) {
curWidthRaw_ = fb->width;
curHeightRaw_ = fb->height;
curWidth_ = fb->width;
curHeight_ = fb->height;
} else {
curWidth_ = vulkan_->GetBackbufferWidth();
curHeight_ = vulkan_->GetBackbufferHeight();
curWidthRaw_ = vulkan_->GetBackbufferWidth();
curHeightRaw_ = vulkan_->GetBackbufferHeight();
if (g_display_rotation == DisplayRotation::ROTATE_90 || g_display_rotation == DisplayRotation::ROTATE_270) {
curWidth_ = curHeightRaw_;
curHeight_ = curWidthRaw_;
} else {
curWidth_ = curWidthRaw_;
curHeight_ = curHeightRaw_;
}
}
if (color == VKRRenderPassAction::CLEAR || depth == VKRRenderPassAction::CLEAR || stencil == VKRRenderPassAction::CLEAR) {
curRenderArea_.SetRect(0, 0, curWidth_, curHeight_);
}
// See above - we add a clear afterward if only one side for depth/stencil CLEAR/KEEP.
@@ -649,6 +695,8 @@ bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, VkIma
}
}
EndCurRenderStep();
VKRStep *step = new VKRStep{ VKRStepType::READBACK };
step->readback.aspectMask = aspectBits;
step->readback.src = src;
@@ -658,8 +706,6 @@ bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, VkIma
step->tag = tag;
steps_.push_back(step);
curRenderStep_ = nullptr;
FlushSync();
Draw::DataFormat srcFormat = Draw::DataFormat::UNDEFINED;
@@ -704,6 +750,9 @@ bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, VkIma
void VulkanRenderManager::CopyImageToMemorySync(VkImage image, int mipLevel, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag) {
_dbg_assert_(insideFrame_);
EndCurRenderStep();
VKRStep *step = new VKRStep{ VKRStepType::READBACK_IMAGE };
step->readback_image.image = image;
step->readback_image.srcRect.offset = { x, y };
@@ -712,8 +761,6 @@ void VulkanRenderManager::CopyImageToMemorySync(VkImage image, int mipLevel, int
step->tag = tag;
steps_.push_back(step);
curRenderStep_ = nullptr;
FlushSync();
// Need to call this after FlushSync so the pixels are guaranteed to be ready in CPU-accessible VRAM.
@@ -927,6 +974,8 @@ void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearSten
data.clear.clearMask = clearMask;
curRenderStep_->commands.push_back(data);
}
curRenderArea_.SetRect(0, 0, curWidth_, curHeight_);
}
void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, VkImageAspectFlags aspectMask, const char *tag) {
@@ -975,6 +1024,8 @@ void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect,
}
}
EndCurRenderStep();
VKRStep *step = new VKRStep{ VKRStepType::COPY };
step->copy.aspectMask = aspectMask;
@@ -990,7 +1041,6 @@ void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect,
std::unique_lock<std::mutex> lock(mutex_);
steps_.push_back(step);
curRenderStep_ = nullptr;
}
void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkImageAspectFlags aspectMask, VkFilter filter, const char *tag) {
@@ -1019,6 +1069,8 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect,
}
}
EndCurRenderStep();
VKRStep *step = new VKRStep{ VKRStepType::BLIT };
step->blit.aspectMask = aspectMask;
@@ -1035,7 +1087,6 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect,
std::unique_lock<std::mutex> lock(mutex_);
steps_.push_back(step);
curRenderStep_ = nullptr;
}
VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBit, int attachment) {
@@ -1078,7 +1129,7 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in
}
void VulkanRenderManager::Finish() {
curRenderStep_ = nullptr;
EndCurRenderStep();
// Let's do just a bit of cleanup on render commands now.
for (auto &step : steps_) {
+75 -3
View File
@@ -61,6 +61,53 @@ enum {
MAX_TIMESTAMP_QUERIES = 128,
};
struct BoundingRect {
int x1;
int y1;
int x2;
int y2;
BoundingRect() {
Reset();
}
void Reset() {
x1 = 65535;
y1 = 65535;
x2 = -65535;
y2 = -65535;
}
bool Empty() const {
return x2 < 0;
}
void SetRect(int x, int y, int width, int height) {
x1 = x;
y1 = y;
x2 = width;
y2 = height;
}
void Apply(const VkRect2D &rect) {
if (rect.offset.x < x1) x1 = rect.offset.x;
if (rect.offset.y < y1) y1 = rect.offset.y;
int rect_x2 = rect.offset.x + rect.extent.width;
int rect_y2 = rect.offset.y + rect.extent.height;
if (rect_x2 > x2) x2 = rect_x2;
if (rect_y2 > y2) y2 = rect_y2;
}
VkRect2D ToVkRect2D() const {
VkRect2D rect;
rect.offset.x = x1;
rect.offset.y = y1;
rect.extent.width = x2 - x1;
rect.extent.height = y2 - y1;
return rect;
}
};
class VulkanRenderManager {
public:
VulkanRenderManager(VulkanContext *vulkan);
@@ -104,11 +151,12 @@ public:
void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, VkImageAspectFlags aspectMask, const char *tag);
void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkImageAspectFlags aspectMask, VkFilter filter, const char *tag);
void BindPipeline(VkPipeline pipeline) {
void BindPipeline(VkPipeline pipeline, PipelineFlags flags) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
_dbg_assert_(pipeline != VK_NULL_HANDLE);
VkRenderData data{ VKRRenderCommand::BIND_PIPELINE };
data.pipeline.pipeline = pipeline;
curPipelineFlags_ |= flags;
curRenderStep_->commands.push_back(data);
}
@@ -130,10 +178,22 @@ public:
curStepHasViewport_ = true;
}
void SetScissor(const VkRect2D &rc) {
void SetScissor(VkRect2D rc) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
_dbg_assert_((int)rc.extent.width >= 0);
_dbg_assert_((int)rc.extent.height >= 0);
// Clamp to curWidth_/curHeight_. Apparently an issue.
if ((int)(rc.offset.x + rc.extent.width) > curWidth_) {
rc.extent.width = curWidth_ - rc.offset.x;
}
if ((int)(rc.offset.y + rc.extent.height) > curHeight_) {
rc.extent.height = curHeight_ - rc.offset.y;
}
_dbg_assert_((int)(rc.offset.x + rc.extent.width) <= curWidth_);
_dbg_assert_((int)(rc.offset.y + rc.extent.height) <= curHeight_);
curRenderArea_.Apply(rc);
VkRenderData data{ VKRRenderCommand::SCISSOR };
data.scissor.scissor = rc;
curRenderStep_->commands.push_back(data);
@@ -229,7 +289,7 @@ public:
return renderStepOffset_ + (int)steps_.size();
}
void CreateBackbuffers();
bool CreateBackbuffers();
void DestroyBackbuffers();
bool HasBackbuffers() {
@@ -265,6 +325,8 @@ public:
private:
bool InitBackbufferFramebuffers(int width, int height);
bool InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering.
void EndCurRenderStep();
void BeginSubmitFrame(int frame);
void EndSubmitFrame(int frame);
void Submit(int frame, bool triggerFence);
@@ -320,14 +382,24 @@ private:
int outOfDateFrames_ = 0;
// Submission time state
// Note: These are raw backbuffer-sized. Rotated.
int curWidthRaw_ = -1;
int curHeightRaw_ = -1;
// Pre-rotation (as you'd expect).
int curWidth_ = -1;
int curHeight_ = -1;
bool insideFrame_ = false;
// This is the offset within this frame, in case of a mid-frame sync.
int renderStepOffset_ = 0;
VKRStep *curRenderStep_ = nullptr;
bool curStepHasViewport_ = false;
bool curStepHasScissor_ = false;
u32 curPipelineFlags_ = 0;
BoundingRect curRenderArea_;
std::vector<VKRStep *> steps_;
bool splitSubmit_ = false;
+41 -23
View File
@@ -171,13 +171,11 @@ public:
VkShaderStageFlagBits StageToVulkan(ShaderStage stage) {
switch (stage) {
case ShaderStage::VERTEX: return VK_SHADER_STAGE_VERTEX_BIT;
case ShaderStage::GEOMETRY: return VK_SHADER_STAGE_GEOMETRY_BIT;
case ShaderStage::COMPUTE: return VK_SHADER_STAGE_COMPUTE_BIT;
case ShaderStage::EVALUATION: return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
case ShaderStage::CONTROL: return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
case ShaderStage::Vertex: return VK_SHADER_STAGE_VERTEX_BIT;
case ShaderStage::Geometry: return VK_SHADER_STAGE_GEOMETRY_BIT;
case ShaderStage::Compute: return VK_SHADER_STAGE_COMPUTE_BIT;
default:
case ShaderStage::FRAGMENT: return VK_SHADER_STAGE_FRAGMENT_BIT;
case ShaderStage::Fragment: return VK_SHADER_STAGE_FRAGMENT_BIT;
}
}
@@ -215,7 +213,9 @@ bool VKShaderModule::Compile(VulkanContext *vulkan, ShaderLanguage language, con
// We'll need this to free it later.
source_ = (const char *)data;
std::vector<uint32_t> spirv;
if (!GLSLtoSPV(vkstage_, source_.c_str(), spirv)) {
std::string errorMessage;
if (!GLSLtoSPV(vkstage_, source_.c_str(), GLSLVariant::VULKAN, spirv, &errorMessage)) {
INFO_LOG(G3D, "Shader compile to module failed: %s", errorMessage.c_str());
return false;
}
@@ -245,7 +245,7 @@ public:
class VKPipeline : public Pipeline {
public:
VKPipeline(VulkanContext *vulkan, size_t size) : vulkan_(vulkan) {
VKPipeline(VulkanContext *vulkan, size_t size, PipelineFlags _flags) : vulkan_(vulkan), flags(_flags) {
uboSize_ = (int)size;
ubo_ = new uint8_t[uboSize_];
}
@@ -274,6 +274,8 @@ public:
VkPipeline backbufferPipeline = VK_NULL_HANDLE;
VkPipeline framebufferPipeline = VK_NULL_HANDLE;
PipelineFlags flags;
int stride[4]{};
int dynamicUniformSize = 0;
@@ -463,6 +465,8 @@ public:
uint64_t GetNativeObject(NativeObject obj) override {
switch (obj) {
case NativeObject::CONTEXT:
return (uint64_t)vulkan_;
case NativeObject::FRAMEBUFFER_RENDERPASS:
// Return a representative renderpass.
return (uint64_t)renderManager_.GetFramebufferRenderPass();
@@ -495,7 +499,6 @@ public:
void InvalidateCachedState() override;
private:
VulkanTexture *GetNullTexture();
VulkanContext *vulkan_ = nullptr;
@@ -758,6 +761,8 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushBuffer *push, const Textur
VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit)
: vulkan_(vulkan), caps_{}, renderManager_(vulkan) {
shaderLanguageDesc_.Init(GLSL_VULKAN);
caps_.anisoSupported = vulkan->GetDeviceFeatures().enabled.samplerAnisotropy != 0;
caps_.geometryShaderSupported = vulkan->GetDeviceFeatures().enabled.geometryShader != 0;
caps_.tesselationShaderSupported = vulkan->GetDeviceFeatures().enabled.tessellationShader != 0;
@@ -778,8 +783,7 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit)
case VULKAN_VENDOR_NVIDIA: caps_.vendor = GPUVendor::VENDOR_NVIDIA; break;
case VULKAN_VENDOR_QUALCOMM: caps_.vendor = GPUVendor::VENDOR_QUALCOMM; break;
case VULKAN_VENDOR_INTEL: caps_.vendor = GPUVendor::VENDOR_INTEL; break;
default:
caps_.vendor = GPUVendor::VENDOR_UNKNOWN;
default: caps_.vendor = GPUVendor::VENDOR_UNKNOWN; break;
}
if (caps_.vendor == GPUVendor::VENDOR_QUALCOMM) {
@@ -993,11 +997,7 @@ VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) {
if (key.imageViews_[i] && key.samplers_[i] && key.samplers_[i]->GetSampler()) {
imageDesc[i].imageView = key.imageViews_[i];
imageDesc[i].sampler = key.samplers_[i]->GetSampler();
#ifdef VULKAN_USE_GENERAL_LAYOUT_FOR_COLOR
imageDesc[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
#else
imageDesc[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
#endif
writes[numWrites].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writes[numWrites].dstSet = descSet;
writes[numWrites].dstArrayElement = 0;
@@ -1018,13 +1018,24 @@ VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) {
}
Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
VKPipeline *pipeline = new VKPipeline(vulkan_, desc.uniformDesc ? desc.uniformDesc->uniformBufferSize : 16 * sizeof(float));
VKInputLayout *input = (VKInputLayout *)desc.inputLayout;
VKBlendState *blend = (VKBlendState *)desc.blend;
VKDepthStencilState *depth = (VKDepthStencilState *)desc.depthStencil;
VKRasterState *raster = (VKRasterState *)desc.raster;
for (int i = 0; i < (int)input->bindings.size(); i++) {
pipeline->stride[i] = input->bindings[i].stride;
u32 pipelineFlags = 0;
if (depth->info.depthTestEnable || depth->info.stencilTestEnable) {
pipelineFlags |= PIPELINE_FLAG_USES_DEPTH_STENCIL;
}
VKPipeline *pipeline = new VKPipeline(vulkan_, desc.uniformDesc ? desc.uniformDesc->uniformBufferSize : 16 * sizeof(float), (PipelineFlags)pipelineFlags);
if (input) {
for (int i = 0; i < (int)input->bindings.size(); i++) {
pipeline->stride[i] = input->bindings[i].stride;
}
} else {
pipeline->stride[0] = 0;
}
std::vector<VkPipelineShaderStageCreateInfo> stages;
@@ -1069,6 +1080,8 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
raster->ToVulkan(&rs);
VkPipelineVertexInputStateCreateInfo emptyVisc{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
VkGraphicsPipelineCreateInfo createInfo[2]{};
for (auto &info : createInfo) {
info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
@@ -1081,7 +1094,7 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
info.pInputAssemblyState = &inputAssembly;
info.pTessellationState = nullptr;
info.pMultisampleState = &ms;
info.pVertexInputState = &input->visc;
info.pVertexInputState = input ? &input->visc : &emptyVisc;
info.pRasterizationState = &rs;
info.pViewportState = &vs; // Must set viewport and scissor counts even if we set the actual state dynamically.
info.layout = pipelineLayout_;
@@ -1340,9 +1353,9 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) {
void VKContext::BindCompatiblePipeline() {
VkRenderPass renderPass = renderManager_.GetCompatibleRenderPass();
if (renderPass == renderManager_.GetBackbufferRenderPass()) {
renderManager_.BindPipeline(curPipeline_->backbufferPipeline);
renderManager_.BindPipeline(curPipeline_->backbufferPipeline, curPipeline_->flags);
} else {
renderManager_.BindPipeline(curPipeline_->framebufferPipeline);
renderManager_.BindPipeline(curPipeline_->framebufferPipeline, curPipeline_->flags);
}
}
@@ -1443,7 +1456,11 @@ uint32_t VKContext::GetDataFormatSupport(DataFormat fmt) const {
// use this frame's init command buffer.
class VKFramebuffer : public Framebuffer {
public:
VKFramebuffer(VKRFramebuffer *fb) : buf_(fb) { _assert_msg_(fb, "Null fb in VKFramebuffer constructor"); }
VKFramebuffer(VKRFramebuffer *fb) : buf_(fb) {
_assert_msg_(fb, "Null fb in VKFramebuffer constructor");
width_ = fb->width;
height_ = fb->height;
}
~VKFramebuffer() {
_assert_msg_(buf_, "Null buf_ in VKFramebuffer - double delete?");
buf_->vulkan_->Delete().QueueCallback([](void *fb) {
@@ -1526,7 +1543,8 @@ void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPass
void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) {
VKFramebuffer *fb = (VKFramebuffer *)fbo;
// TODO: There are cases where this is okay, actually.
// TODO: There are cases where this is okay, actually. But requires layout transitions and stuff -
// we're not ready for this.
_assert_(fb != curFramebuffer_);
int aspect = 0;
+12 -12
View File
@@ -101,7 +101,7 @@ bool RefCountedObject::ReleaseAssertLast() {
// The Vulkan ones can be re-used with modern GL later if desired, as they're just GLSL.
static const std::vector<ShaderSource> fsTexCol = {
{ShaderLanguage::GLSL_ES_200,
{ShaderLanguage::GLSL_1xx,
"#ifdef GL_ES\n"
"precision lowp float;\n"
"#endif\n"
@@ -145,7 +145,7 @@ static const std::vector<ShaderSource> fsTexCol = {
};
static const std::vector<ShaderSource> fsTexColRBSwizzle = {
{ShaderLanguage::GLSL_ES_200,
{GLSL_1xx,
"#ifdef GL_ES\n"
"precision lowp float;\n"
"#endif\n"
@@ -189,7 +189,7 @@ static const std::vector<ShaderSource> fsTexColRBSwizzle = {
};
static const std::vector<ShaderSource> fsCol = {
{ ShaderLanguage::GLSL_ES_200,
{ GLSL_1xx,
"#ifdef GL_ES\n"
"precision lowp float;\n"
"#endif\n"
@@ -226,7 +226,7 @@ static const std::vector<ShaderSource> fsCol = {
// ================================== VERTEX SHADERS
static const std::vector<ShaderSource> vsCol = {
{ ShaderLanguage::GLSL_ES_200,
{ GLSL_1xx,
"#if __VERSION__ >= 130\n"
"#define attribute in\n"
"#define varying out\n"
@@ -288,7 +288,7 @@ const UniformBufferDesc vsColBufDesc { sizeof(VsColUB), {
} };
static const std::vector<ShaderSource> vsTexCol = {
{ ShaderLanguage::GLSL_ES_200,
{ GLSL_1xx,
"#if __VERSION__ >= 130\n"
"#define attribute in\n"
"#define varying out\n"
@@ -367,12 +367,12 @@ ShaderModule *CreateShader(DrawContext *draw, ShaderStage stage, const std::vect
}
bool DrawContext::CreatePresets() {
vsPresets_[VS_TEXTURE_COLOR_2D] = CreateShader(this, ShaderStage::VERTEX, vsTexCol);
vsPresets_[VS_COLOR_2D] = CreateShader(this, ShaderStage::VERTEX, vsCol);
vsPresets_[VS_TEXTURE_COLOR_2D] = CreateShader(this, ShaderStage::Vertex, vsTexCol);
vsPresets_[VS_COLOR_2D] = CreateShader(this, ShaderStage::Vertex, vsCol);
fsPresets_[FS_TEXTURE_COLOR_2D] = CreateShader(this, ShaderStage::FRAGMENT, fsTexCol);
fsPresets_[FS_COLOR_2D] = CreateShader(this, ShaderStage::FRAGMENT, fsCol);
fsPresets_[FS_TEXTURE_COLOR_2D_RB_SWIZZLE] = CreateShader(this, ShaderStage::FRAGMENT, fsTexColRBSwizzle);
fsPresets_[FS_TEXTURE_COLOR_2D] = CreateShader(this, ShaderStage::Fragment, fsTexCol);
fsPresets_[FS_COLOR_2D] = CreateShader(this, ShaderStage::Fragment, fsCol);
fsPresets_[FS_TEXTURE_COLOR_2D_RB_SWIZZLE] = CreateShader(this, ShaderStage::Fragment, fsTexColRBSwizzle);
return vsPresets_[VS_TEXTURE_COLOR_2D] && vsPresets_[VS_COLOR_2D] && fsPresets_[FS_TEXTURE_COLOR_2D] && fsPresets_[FS_COLOR_2D] && fsPresets_[FS_TEXTURE_COLOR_2D_RB_SWIZZLE];
}
@@ -396,7 +396,7 @@ DrawContext::~DrawContext() {
DestroyPresets();
}
// TODO: SSE/NEON
// TODO: Use the functions we have in Common/ColorConv.cpp.
// 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) {
// Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP.
@@ -455,7 +455,7 @@ void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, u
}
}
// TODO: SSE/NEON
// TODO: Use the functions we have in Common/ColorConv.cpp.
// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :)
void ConvertFromBGRA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format) {
// Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP.
+18 -50
View File
@@ -12,7 +12,8 @@
#include <string>
#include <vector>
#include "DataFormat.h"
#include "Common/GPU/DataFormat.h"
#include "Common/GPU/Shader.h"
namespace Lin {
class Matrix4x4;
@@ -161,15 +162,6 @@ enum class TextureType : uint8_t {
ARRAY2D,
};
enum class ShaderStage {
VERTEX,
FRAGMENT,
GEOMETRY,
CONTROL, // HULL
EVALUATION, // DOMAIN
COMPUTE,
};
enum class CullMode : uint8_t {
NONE,
FRONT,
@@ -203,15 +195,6 @@ enum class TextureAddressMode {
CLAMP_TO_BORDER,
};
enum class ShaderLanguage {
GLSL_ES_200 = 1,
GLSL_ES_300 = 2,
GLSL_410 = 4,
GLSL_VULKAN = 8,
HLSL_D3D9 = 32,
HLSL_D3D11 = 64,
};
enum FormatSupport {
FMT_RENDERTARGET = 1,
FMT_TEXTURE = 2,
@@ -261,13 +244,6 @@ enum class NativeObject {
NULL_IMAGEVIEW,
};
enum FBColorDepth {
FBO_8888,
FBO_565,
FBO_4444,
FBO_5551,
};
enum FBChannel {
FB_COLOR_BIT = 1,
FB_DEPTH_BIT = 2,
@@ -309,7 +285,6 @@ struct FramebufferDesc {
int depth;
int numColorAttachments;
bool z_stencil;
FBColorDepth colorDepth;
const char *tag; // For graphics debuggers
};
@@ -372,6 +347,10 @@ public:
class Framebuffer : public RefCountedObject {
public:
int Width() { return width_; }
int Height() { return height_; }
protected:
int width_ = -1, height_ = -1;
};
class Buffer : public RefCountedObject {
@@ -384,7 +363,7 @@ public:
int Height() { return height_; }
int Depth() { return depth_; }
protected:
int width_, height_, depth_;
int width_ = -1, height_ = -1, depth_ = -1;
};
struct BindingDesc {
@@ -406,28 +385,7 @@ struct InputLayoutDesc {
class InputLayout : public RefCountedObject { };
enum class UniformType : int8_t {
FLOAT1,
FLOAT2,
FLOAT3,
FLOAT4,
MATRIX4X4,
};
// For emulation of uniform buffers on D3D9/GL
struct UniformDesc {
const char *name; // For GL
int16_t vertexReg; // For D3D
int16_t fragmentReg; // For D3D
UniformType type;
int16_t offset;
// TODO: Support array elements etc.
};
struct UniformBufferDesc {
size_t uniformBufferSize;
std::vector<UniformDesc> uniforms;
};
// Uniform types have moved to Shader.h.
class ShaderModule : public RefCountedObject {
public:
@@ -521,6 +479,7 @@ struct DeviceCaps {
bool framebufferBlitSupported;
bool framebufferDepthCopySupported;
bool framebufferDepthBlitSupported;
bool framebufferFetchSupported;
std::string deviceName; // The device name to use when creating the thin3d context, to get the same one.
};
@@ -573,8 +532,15 @@ public:
virtual std::vector<std::string> GetExtensionList() const { return std::vector<std::string>(); }
virtual std::vector<std::string> GetDeviceList() const { return std::vector<std::string>(); }
// Describes the primary shader language that this implementation prefers.
const ShaderLanguageDesc &GetShaderLanguageDesc() {
return shaderLanguageDesc_;
}
virtual uint32_t GetSupportedShaderLanguages() const = 0;
virtual void SetErrorCallback(ErrorCallbackFn callback, void *userdata) {}
// Partial pipeline state, used to create pipelines. (in practice, in d3d11 they'll use the native state objects directly).
virtual DepthStencilState *CreateDepthStencilState(const DepthStencilStateDesc &desc) = 0;
virtual BlendState *CreateBlendState(const BlendStateDesc &desc) = 0;
@@ -686,6 +652,8 @@ protected:
ShaderModule *vsPresets_[VS_MAX_PRESET];
ShaderModule *fsPresets_[FS_MAX_PRESET];
ShaderLanguageDesc shaderLanguageDesc_;
int targetWidth_;
int targetHeight_;
+1
View File
@@ -18,6 +18,7 @@
#include "ppsspp_config.h"
#include <cstring>
#include <cstdlib>
#include "Common/Common.h"
#include "Common/Log.h"
+2 -1
View File
@@ -24,10 +24,12 @@
#include <unistd.h>
#endif
#include "Common/Log.h"
#include "Common/TimeUtil.h"
namespace net {
void Init()
{
#ifdef _WIN32
@@ -45,7 +47,6 @@ void Shutdown()
}
bool DNSResolve(const std::string &host, const std::string &service, addrinfo **res, std::string &error, DNSType type) {
#if PPSSPP_PLATFORM(SWITCH)
// Force IPv4 lookups.
if (type == DNSType::IPV6) {
+1
View File
@@ -5,6 +5,7 @@
#include <cstdio>
#include <cstdarg>
#include <cstdint>
#include <cstdlib>
#include <vector>
#if defined(_MSC_VER) && _MSC_VER < 1900
+2
View File
@@ -31,6 +31,8 @@
#include <string>
#include <vector>
#include <cstdlib>
#include "Common/CommonTypes.h"
#include "Common/Log.h"
+2
View File
@@ -99,3 +99,5 @@ float System_GetPropertyFloat(SystemProperty prop);
bool System_GetPropertyBool(SystemProperty prop);
std::vector<std::string> __cameraGetDeviceList();
bool audioRecording_Available();
bool audioRecording_State();
+4
View File
@@ -74,6 +74,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) {
CheckSetting(iniFile, gameID, "AdhocHackStopFriendClear", &flags_.AdhocHackStopFriendClear);
CheckSetting(iniFile, gameID, "AdhocHackPhantasyStarOffset", &flags_.AdhocHackPhantasyStarOffset);
CheckSetting(iniFile, gameID, "BlockTransferAllowCreateFB", &flags_.BlockTransferAllowCreateFB);
CheckSetting(iniFile, gameID, "IntraVRAMBlockTransferAllowCreateFB", &flags_.IntraVRAMBlockTransferAllowCreateFB);
CheckSetting(iniFile, gameID, "YugiohSaveFix", &flags_.YugiohSaveFix);
CheckSetting(iniFile, gameID, "ForceUMDDelay", &flags_.ForceUMDDelay);
CheckSetting(iniFile, gameID, "ForceMax60FPS", &flags_.ForceMax60FPS);
@@ -83,6 +84,9 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) {
CheckSetting(iniFile, gameID, "ForceSoftwareRenderer", &flags_.ForceSoftwareRenderer);
CheckSetting(iniFile, gameID, "DarkStalkersPresentHack", &flags_.DarkStalkersPresentHack);
CheckSetting(iniFile, gameID, "ReportSmallMemstick", &flags_.ReportSmallMemstick);
CheckSetting(iniFile, gameID, "MemstickFixedFree", &flags_.MemstickFixedFree);
CheckSetting(iniFile, gameID, "DateLimited", &flags_.DateLimited);
CheckSetting(iniFile, gameID, "ReinterpretFramebuffers", &flags_.ReinterpretFramebuffers);
}
void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) {
+4
View File
@@ -72,6 +72,7 @@ struct CompatFlags {
bool AdhocHackStopFriendClear;
bool AdhocHackPhantasyStarOffset;
bool BlockTransferAllowCreateFB;
bool IntraVRAMBlockTransferAllowCreateFB;
bool YugiohSaveFix;
bool ForceUMDDelay;
bool ForceMax60FPS;
@@ -81,6 +82,9 @@ struct CompatFlags {
bool ForceSoftwareRenderer;
bool DarkStalkersPresentHack;
bool ReportSmallMemstick;
bool MemstickFixedFree;
bool DateLimited;
bool ReinterpretFramebuffers;
};
class IniFile;
+2 -1
View File
@@ -467,6 +467,7 @@ static ConfigSetting generalSettings[] = {
ConfigSetting("EnableStateUndo", &g_Config.bEnableStateUndo, false/*&DefaultEnableStateUndo*/, true, true),
ConfigSetting("RewindFlipFrequency", &g_Config.iRewindFlipFrequency, 0, true, true),
ConfigSetting("ShowOnScreenMessage", &g_Config.bShowOnScreenMessages, true, true, false),
ConfigSetting("ShowRegionOnGameIcon", &g_Config.bShowRegionOnGameIcon, false),
ConfigSetting("ShowIDOnGameIcon", &g_Config.bShowIDOnGameIcon, false),
ConfigSetting("GameGridScale", &g_Config.fGameGridScale, 1.0),
@@ -986,7 +987,7 @@ static ConfigSetting networkSettings[] = {
ConfigSetting("EnableAdhocServer", &g_Config.bEnableAdhocServer, false, true, true),
ConfigSetting("proAdhocServer", &g_Config.proAdhocServer, "localhost", true, true),
ConfigSetting("PortOffset", &g_Config.iPortOffset, 0, true, true),
ConfigSetting("MinTimeout", &g_Config.iMinTimeout, 1, true, true),
ConfigSetting("MinTimeout", &g_Config.iMinTimeout, 0, true, true),
ConfigSetting("TCPNoDelay", &g_Config.bTCPNoDelay, false, true, true),
ConfigSetting("ForcedFirstConnect", &g_Config.bForcedFirstConnect, false, true, true),
ConfigSetting("EnableUPnP", &g_Config.bEnableUPnP, false, true, true),
+1
View File
@@ -247,6 +247,7 @@ public:
bool bShowRegionOnGameIcon;
bool bShowIDOnGameIcon;
float fGameGridScale;
bool bShowOnScreenMessages;
// TODO: Maybe move to a separate theme system.
uint32_t uItemStyleFg;
+3 -2
View File
@@ -66,11 +66,12 @@ struct CoreParameter {
std::string *collectEmuLog = nullptr;
bool headLess; // Try to avoid messageboxes etc
// Internal PSP resolution
// Internal PSP rendering resolution and scale factor.
int renderScaleFactor;
int renderWidth;
int renderHeight;
// Actual pixel output resolution (for use by glViewport and the like)
// Actual output resolution in pixels.
int pixelWidth;
int pixelHeight;
+13 -1
View File
@@ -410,8 +410,10 @@ int ElfReader::LoadInto(u32 loadAddress, bool fromTop)
for (int i = 0; i < header->e_phnum; i++) {
const Elf32_Phdr *p = &segments[i];
if (p->p_type == PT_LOAD) {
if (p->p_vaddr < totalStart)
if (p->p_vaddr < totalStart) {
totalStart = p->p_vaddr;
firstSegAlign = p->p_align;
}
if (p->p_vaddr + p->p_memsz > totalEnd)
totalEnd = p->p_vaddr + p->p_memsz;
}
@@ -612,6 +614,16 @@ u32 ElfReader::GetTotalTextSize() const {
return total;
}
u32 ElfReader::GetTotalTextSizeFromSeg() const {
u32 total = 0;
for (int i = 0; i < GetNumSegments(); ++i) {
if ((segments[i].p_flags & PF_X) != 0) {
total += segments[i].p_filesz;
}
}
return total;
}
u32 ElfReader::GetTotalDataSize() const {
u32 total = 0;
for (int i = 0; i < GetNumSections(); ++i) {
+6
View File
@@ -114,6 +114,10 @@ public:
return segments[segment].p_memsz;
}
u32 GetFirstSegmentAlign() const {
return firstSegAlign;
}
bool DidRelocate() const {
return bRelocate;
}
@@ -127,6 +131,7 @@ public:
}
u32 GetTotalTextSize() const;
u32 GetTotalTextSizeFromSeg() const;
u32 GetTotalDataSize() const;
u32 GetTotalSectionSizeByPrefix(const std::string &prefix) const;
@@ -151,4 +156,5 @@ private:
u32 vaddr = 0;
u32 segmentVAddr[32];
size_t size_ = 0;
u32 firstSegAlign = 0;
};
+76 -28
View File
@@ -21,6 +21,10 @@
// This is a direct port of Coldbird's code from http://code.google.com/p/aemu/
// All credit goes to him!
#if defined(_WIN32)
#include "Common/CommonWindows.h"
#endif
#if !defined(_WIN32)
#include <unistd.h>
#include <netinet/tcp.h>
@@ -62,6 +66,7 @@ SceNetAdhocctlPeerInfo * friends = NULL;
SceNetAdhocctlScanInfo * networks = NULL;
SceNetAdhocctlScanInfo * newnetworks = NULL;
u64 adhocctlStartTime = 0;
bool isAdhocctlBusy = false;
int adhocctlState = ADHOCCTL_STATE_DISCONNECTED;
int adhocctlCurrentMode = ADHOCCTL_MODE_NONE;
int adhocConnectionType = ADHOC_CONNECT;
@@ -315,7 +320,7 @@ void changeBlockingMode(int fd, int nonblocking) {
#endif
}
int countAvailableNetworks() {
int countAvailableNetworks(const bool excludeSelf) {
// Network Count
int count = 0;
@@ -323,7 +328,7 @@ int countAvailableNetworks() {
SceNetAdhocctlScanInfo * group = networks;
// Count Groups
for (; group != NULL; group = group->next) count++;
for (; group != NULL && (!excludeSelf || !isLocalMAC(&group->bssid.mac_addr)); group = group->next) count++;
// Return Network Count
return count;
@@ -983,8 +988,8 @@ void handleTimeout(SceNetAdhocMatchingContext * context)
SceNetAdhocMatchingMemberInternal * next = peer->next;
u64_le now = CoreTiming::GetGlobalTimeUsScaled(); //time_now_d()*1000000.0
// Timeout!
if (peer->state != 0 && (now - peer->lastping) > context->timeout)
// Timeout! Apparently the latest GetGlobalTimeUsScaled (ie. now) have a possibility to be smaller than previous GetGlobalTimeUsScaled (ie. lastping) thus resulting a negative number when subtracted :(
if (peer->state != 0 && static_cast<s64>(now - peer->lastping) > static_cast<s64>(context->timeout))
{
// Spawn Timeout Event
if ((context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && peer->state == PSP_ADHOC_MATCHING_PEER_PARENT) ||
@@ -993,7 +998,7 @@ void handleTimeout(SceNetAdhocMatchingContext * context)
// FIXME: TIMEOUT event should only be triggered on Parent/P2P mode and for Parent/P2P peer?
spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_TIMEOUT, &peer->mac, 0, NULL);
INFO_LOG(SCENET, "TimedOut Member Peer %s (%lldms)", mac2str(&peer->mac).c_str(), (context->timeout / 1000));
INFO_LOG(SCENET, "TimedOut Member Peer %s (%lld - %lld = %lld > %lld us)", mac2str(&peer->mac).c_str(), now, peer->lastping, (now - peer->lastping), context->timeout);
if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT)
sendDeathMessage(context, peer);
@@ -1165,6 +1170,7 @@ void AfterAdhocMipsCall::run(MipsCall& call) {
u32 v0 = currentMIPS->r[MIPS_REG_V0];
if (__IsInInterrupt()) ERROR_LOG(SCENET, "AfterAdhocMipsCall::run [ID=%i][Event=%d] is Returning Inside an Interrupt!", HandlerID, EventID);
SetAdhocctlInCallback(false);
isAdhocctlBusy = false;
DEBUG_LOG(SCENET, "AfterAdhocMipsCall::run [ID=%i][Event=%d] [cbId: %u][retV0: %08x]", HandlerID, EventID, call.cbId, v0);
//call.setReturnValue(v0);
}
@@ -1334,6 +1340,10 @@ int friendFinder(){
if (initNetwork(&product_code) == 0) {
networkInited = true;
INFO_LOG(SCENET, "FriendFinder: Network [RE]Initialized");
// At this point we are most-likely not in a Group within the Adhoc Server, so we should probably reset AdhocctlState
adhocctlState = ADHOCCTL_STATE_DISCONNECTED;
netAdhocGameModeEntered = false;
isAdhocctlBusy = false;
}
else {
networkInited = false;
@@ -1348,7 +1358,7 @@ int friendFinder(){
// Ping Server
now = time_now_d() * 1000000.0; // Use time_now_d()*1000000.0 instead of CoreTiming::GetGlobalTimeUsScaled() if the game gets disconnected from AdhocServer too soon when FPS wasn't stable
// original code : ((sceKernelGetSystemTimeWide() - lastping) >= ADHOCCTL_PING_TIMEOUT)
if (now - lastping >= PSP_ADHOCCTL_PING_TIMEOUT) { // We may need to use lower interval to prevent getting timeout at Pro Adhoc Server through internet
if (static_cast<s64>(now - lastping) >= PSP_ADHOCCTL_PING_TIMEOUT) { // We may need to use lower interval to prevent getting timeout at Pro Adhoc Server through internet
// Prepare Packet
uint8_t opcode = OPCODE_PING;
@@ -1372,7 +1382,7 @@ int friendFinder(){
else {
// Update Ping Time
lastping = now;
DEBUG_LOG(SCENET, "FriendFinder: Sending OPCODE_PING (%llu)", static_cast<unsigned long long>(now));
VERBOSE_LOG(SCENET, "FriendFinder: Sending OPCODE_PING (%llu)", static_cast<unsigned long long>(now));
}
}
}
@@ -1395,6 +1405,12 @@ int friendFinder(){
}
}
// Calculate EnterGameMode Timeout to prevent waiting forever for disconnected players
if (isAdhocctlBusy && adhocctlState == ADHOCCTL_STATE_DISCONNECTED && adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE && netAdhocGameModeEntered && static_cast<s64>(now - adhocctlStartTime) > netAdhocEnterGameModeTimeout) {
netAdhocGameModeEntered = false;
notifyAdhocctlHandlers(ADHOCCTL_EVENT_ERROR, ERROR_NET_ADHOC_TIMEOUT);
}
// Handle Packets
if (rxpos > 0) {
// BSSID Packet
@@ -1419,14 +1435,14 @@ int friendFinder(){
}) == gameModeMacs.end()) {
// Arrange the order to be consistent on all players (Host on top), Starting from our self the rest of new players will be added to the back
gameModeMacs.push_back(localMac);
// FIXME: OPCODE_CONNECT_BSSID only triggered once, but the timing of ADHOCCTL_EVENT_GAME notification could be too soon, since there could be more players that need to join before the event should be notified
if (netAdhocGameModeEntered && gameModeMacs.size() >= requiredGameModeMacs.size()) {
notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0);
}
}
else
WARN_LOG(SCENET, "GameMode SelfMember [%s] Already Existed!", mac2str(&localMac).c_str());
if (gameModeMacs.size() >= requiredGameModeMacs.size()) {
//adhocctlState = ADHOCCTL_STATE_GAMEMODE;
notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0);
}
}
else {
//adhocctlState = ADHOCCTL_STATE_CONNECTED;
@@ -1521,17 +1537,16 @@ int friendFinder(){
it = gameModeMacs.begin() + 1;
gameModeMacs.insert(it, packet->mac);
}
// From JPCSP: Join complete when all the required MACs have joined
if (netAdhocGameModeEntered && requiredGameModeMacs.size() > 0 && gameModeMacs.size() == requiredGameModeMacs.size()) {
// TODO: Should we replace gameModeMacs contents with requiredGameModeMacs contents to make sure they are in the same order with macs from sceNetAdhocctlCreateEnterGameMode? But may not be consistent with the list on client side!
//gameModeMacs = requiredGameModeMacs;
notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0);
}
}
else
WARN_LOG(SCENET, "GameMode Member [%s] Already Existed!", mac2str(&packet->mac).c_str());
// From JPCSP: Join complete when all the required MACs have joined
if (requiredGameModeMacs.size() > 0 && gameModeMacs.size() >= requiredGameModeMacs.size()) {
// TODO: Should we replace gameModeMacs contents with requiredGameModeMacs contents to make sure they are in the same order with macs from sceNetAdhocctlCreateEnterGameMode? But may not be consistent with the list on client side!
//gameModeMacs = requiredGameModeMacs;
//adhocctlState = ADHOCCTL_STATE_GAMEMODE;
notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0);
}
}
// Update HUD User Count
@@ -1572,13 +1587,14 @@ int friendFinder(){
// Log Incoming Peer Delete Request
INFO_LOG(SCENET, "FriendFinder: Incoming Peer Data Delete Request...");
/*if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) {
if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) {
auto peer = findFriendByIP(packet->ip);
gameModeMacs.erase(std::remove_if(gameModeMacs.begin(), gameModeMacs.end(),
[peer](SceNetEtherAddr const& e) {
return isMacMatch(&e, &peer->mac_addr);
}), gameModeMacs.end());
}*/
for (auto& gma : replicaGameModeAreas)
if (isMacMatch(&gma.mac, &peer->mac_addr)) {
gma.updateTimestamp = 0;
break;
}
}
// Delete User by IP, should delete by MAC since IP can be shared (behind NAT) isn't?
deleteFriendByIP(packet->ip);
@@ -1633,7 +1649,8 @@ int friendFinder(){
// Set group parameters
// Since 0 is not a valid active channel we fake the channel for Automatic Channel (JPCSP use 11 as default). Ridge Racer 2 will ignore any groups with channel 0 or that doesn't matched with channel value returned from sceUtilityGetSystemParamInt (which mean sceUtilityGetSystemParamInt must not return channel 0 when connected to a network?)
group->channel = parameter.channel; //(parameter.channel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) ? defaultWlanChannel : parameter.channel;
group->mode = adhocctlCurrentMode;
// This Mode should be a valid mode (>=0), probably should be sent by AdhocServer since there are 2 possibilities (Normal and GameMode). Air Conflicts - Aces Of World War 2 (which use GameMode) seems to relies on this Mode value.
group->mode = std::max(ADHOCCTL_MODE_NORMAL, adhocctlCurrentMode); // default to ADHOCCTL_MODE_NORMAL
// Link into Group List
newnetworks = group;
@@ -1901,6 +1918,11 @@ int setSockBufferSize(int sock, int opt, int size) { // opt = SO_RCVBUF/SO_SNDBU
return setsockopt(sock, SOL_SOCKET, opt, (char *)&n, sizeof(n));
}
int setSockMSS(int sock, int size) {
int mss = size; // 1460;
return setsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, (char*)&mss, sizeof(mss));
}
int setSockTimeout(int sock, int opt, unsigned long timeout_usec) { // opt = SO_SNDTIMEO/SO_RCVTIMEO
if (timeout_usec > 0 && timeout_usec < minSocketTimeoutUS) timeout_usec = minSocketTimeoutUS; // Override timeout for high latency multiplayer
#if defined(_WIN32)
@@ -1923,8 +1945,21 @@ int getSockNoDelay(int tcpsock) {
return opt;
}
//#define TCP_QUICKACK 0x0c
int setSockNoDelay(int tcpsock, int flag) {
int opt = flag;
// Disable ACK Delay when supported
#if defined(TCP_QUICKACK)
setsockopt(tcpsock, IPPROTO_TCP, TCP_QUICKACK, (char*)&opt, sizeof(opt));
#elif defined(_WIN32)
#if !defined(SIO_TCP_SET_ACK_FREQUENCY)
#define SIO_TCP_SET_ACK_FREQUENCY _WSAIOW(IOC_VENDOR,23)
#endif
int freq = flag? 1:2; // can be 1..255, default is 2 (delayed 200ms)
DWORD retbytes = 0;
WSAIoctl(tcpsock, SIO_TCP_SET_ACK_FREQUENCY, &freq, sizeof(freq), NULL, 0, &retbytes, NULL, NULL);
#endif
// Disable Nagle Algo
return setsockopt(tcpsock, IPPROTO_TCP, TCP_NODELAY, (char*)&opt, sizeof(opt));
}
@@ -1950,6 +1985,15 @@ int setSockReuseAddrPort(int sock) {
#if !defined(TCP_KEEPIDLE)
#define TCP_KEEPIDLE TCP_KEEPALIVE //TCP_KEEPIDLE on Linux is equivalent to TCP_KEEPALIVE on macOS
#endif
// VS 2017 compatibility
#if _MSC_VER
#ifndef TCP_KEEPCNT
#define TCP_KEEPCNT 16
#endif
#ifndef TCP_KEEPINTVL
#define TCP_KEEPINTVL 17
#endif
#endif
int setSockKeepAlive(int sock, bool keepalive, const int keepinvl, const int keepcnt, const int keepidle) {
int optval = keepalive ? 1 : 0;
int optlen = sizeof(optval);
@@ -2064,7 +2108,7 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){
parameter.channel = g_Config.iWlanAdhocChannel;
// Assign a Valid Channel when connected to AP/Adhoc if it's Auto. JPCSP use 11 as default for Auto (Commonly for Auto: 1, 6, 11)
if (parameter.channel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) parameter.channel = defaultWlanChannel; // Faked Active channel to default channel
getLocalMac(&parameter.bssid.mac_addr);
//getLocalMac(&parameter.bssid.mac_addr);
// Default ProductId
product_code.type = adhoc_id->type;
@@ -2074,6 +2118,10 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){
if (g_adhocServerIP.in.sin_addr.s_addr == INADDR_NONE)
return -1;
// Don't need to connect if AdhocServer IP is the same with this instance localhost IP and having AdhocServer disabled
if (g_adhocServerIP.in.sin_addr.s_addr == g_localhostIP.in.sin_addr.s_addr && !g_Config.bEnableAdhocServer)
return -1;
// Connect to Adhoc Server
int errorcode = 0;
int cnt = 0;
+42 -14
View File
@@ -59,6 +59,7 @@
#undef ESHUTDOWN
#undef ECONNABORTED
#undef ECONNRESET
#undef ECONNREFUSED
#undef ENOTCONN
#undef EAGAIN
#undef EINPROGRESS
@@ -69,6 +70,7 @@
#define ESHUTDOWN WSAESHUTDOWN
#define ECONNABORTED WSAECONNABORTED
#define ECONNRESET WSAECONNRESET
#define ECONNREFUSED WSAECONNREFUSED
#define ENOTCONN WSAENOTCONN
#define EAGAIN WSAEWOULDBLOCK
#define EINPROGRESS WSAEWOULDBLOCK
@@ -110,15 +112,22 @@ inline bool isDisconnected(int errcode) { return (errcode == EPIPE || errcode ==
// Default GameMode definitions
#define ADHOC_GAMEMODE_PORT 31000
#define GAMEMODE_UPDATE_INTERVAL 10000 // 12000 usec on JPCSP, but 10000 works better on BattleZone (in order to get full speed 60 FPS)
#define GAMEMODE_UPDATE_INTERVAL 500 // 12000 usec on JPCSP, but lower value works better on BattleZone (in order to get full speed 60 FPS)
#define GAMEMODE_INIT_DELAY 10000
#define GAMEMODE_SYNC_TIMEOUT 250000
// GameMode Type
#define ADHOCCTL_GAMETYPE_1A 1
#define ADHOCCTL_GAMETYPE_1B 2
#define ADHOCCTL_GAMETYPE_2A 3
// psp strutcs and definitions
#define ADHOCCTL_MODE_NONE -1
#define ADHOCCTL_MODE_NONE -1 // We only use this internally as initial value before attempting to create/connect/join/scan any group
#define ADHOCCTL_MODE_NORMAL 0 // ADHOCCTL_MODE_ADHOC
#define ADHOCCTL_MODE_GAMEMODE 1
// Event Types for Event Handler
#define ADHOCCTL_EVENT_ERROR 0
#define ADHOCCTL_EVENT_ERROR 0 // Used to pass error code to Adhocctl Handler?
#define ADHOCCTL_EVENT_CONNECT 1
#define ADHOCCTL_EVENT_DISCONNECT 2
#define ADHOCCTL_EVENT_SCAN 3
@@ -138,7 +147,7 @@ inline bool isDisconnected(int errcode) { return (errcode == EPIPE || errcode ==
// ProductType ( extracted from SSID along with ProductId & GroupName, Pattern = "PSP_([AXS])(.........)_([LG])_(.*)" )
#define PSP_ADHOCCTL_TYPE_COMMERCIAL 0
#define PSP_ADHOCCTL_TYPE_DEBUG 1
#define PSP_ADHOCCTL_TYPE_SYSTEM 2
#define PSP_ADHOCCTL_TYPE_SYSTEM 2 // Used for GameSharing?
// Kernel Utility Netconf Adhoc Types
#define UTILITY_NETCONF_TYPE_CONNECT_ADHOC 2
@@ -318,6 +327,7 @@ typedef struct GameModeArea {
//int socket; // PDP socket?
u64 updateTimestamp;
int dataUpdated;
int dataSent;
SceNetEtherAddr mac;
u8* data; // upto "size" bytes started from "addr" ?
} PACK GameModeArea;
@@ -390,8 +400,8 @@ typedef struct SceNetAdhocGameModeBufferStat {
// Adhoc ID (Game Product Key)
#define ADHOCCTL_ADHOCID_LEN 9
typedef struct SceNetAdhocctlAdhocId {
s32_le type;
uint8_t data[ADHOCCTL_ADHOCID_LEN];
s32_le type; // Air Conflicts - Aces Of World War 2 is using 2 for GameSharing?
uint8_t data[ADHOCCTL_ADHOCID_LEN]; // Air Conflicts - Aces Of World War 2 is using "000000001" for GameSharing?
uint8_t padding[3];
} PACK SceNetAdhocctlAdhocId; // should this be packed?
#ifdef _MSC_VER
@@ -557,19 +567,24 @@ typedef struct SceNetAdhocMatchingContext {
// End of psp definitions
enum {
// pspnet_adhoc_auth
ERROR_NET_ADHOC_AUTH_ALREADY_INITIALIZED = 0x80410601,
// pspnet_adhoc
ERROR_NET_ADHOC_INVALID_SOCKET_ID = 0x80410701,
ERROR_NET_ADHOC_INVALID_ADDR = 0x80410702,
ERROR_NET_ADHOC_INVALID_PORT = 0x80410703,
ERROR_NET_ADHOC_INVALID_BUFLEN = 0x80410704,
ERROR_NET_ADHOC_INVALID_DATALEN = 0x80410705,
ERROR_NET_ADHOC_NOT_ENOUGH_SPACE = 0x80400706,
ERROR_NET_ADHOC_NOT_ENOUGH_SPACE = 0x80400706, // not a typo
ERROR_NET_ADHOC_SOCKET_DELETED = 0x80410707,
ERROR_NET_ADHOC_SOCKET_ALERTED = 0x80410708,
ERROR_NET_ADHOC_WOULD_BLOCK = 0x80410709, //ERROR_NET_ADHOC_NO_DATA_AVAILABLE
ERROR_NET_ADHOC_PORT_IN_USE = 0x8041070a,
ERROR_NET_ADHOC_NOT_CONNECTED = 0x8041070B,
ERROR_NET_ADHOC_DISCONNECTED = 0x8041070c,
ERROR_NET_ADHOC_NOT_OPENED = 0x8040070D,
ERROR_NET_ADHOC_NOT_LISTENED = 0x8040070E,
ERROR_NET_ADHOC_NOT_OPENED = 0x8040070D, // not a typo
ERROR_NET_ADHOC_NOT_LISTENED = 0x8040070E, // not a typo
ERROR_NET_ADHOC_SOCKET_ID_NOT_AVAIL = 0x8041070F,
ERROR_NET_ADHOC_PORT_NOT_AVAIL = 0x80410710,
ERROR_NET_ADHOC_INVALID_ARG = 0x80410711,
@@ -585,7 +600,9 @@ enum {
ERROR_NET_ADHOC_NOT_IN_GAMEMODE = 0x8041071B,
ERROR_NET_ADHOC_NOT_CREATED = 0x8041071C,
// pspnet_adhoc_matching
ERROR_NET_ADHOC_MATCHING_INVALID_MODE = 0x80410801,
ERROR_NET_ADHOC_MATCHING_INVALID_PORT = 0x80410802,
ERROR_NET_ADHOC_MATCHING_INVALID_MAXNUM = 0x80410803,
ERROR_NET_ADHOC_MATCHING_RXBUF_TOO_SHORT = 0x80410804,
ERROR_NET_ADHOC_MATCHING_INVALID_OPTLEN = 0x80410805,
@@ -609,21 +626,26 @@ enum {
ERROR_NET_ADHOC_MATCHING_NOT_ESTABLISHED = 0x80410817,
ERROR_NET_ADHOC_MATCHING_DATA_BUSY = 0x80410818,
// pspnet_adhocctl
ERROR_NET_ADHOCCTL_NOT_LEFT_IBSS = 0x80410b01,
ERROR_NET_ADHOCCTL_ALREADY_CONNECTED = 0x80410b02,
ERROR_NET_ADHOCCTL_WLAN_SWITCH_OFF = 0x80410b03,
ERROR_NET_ADHOCCTL_INVALID_ARG = 0x80410B04,
ERROR_NET_ADHOCCTL_TIMEOUT = 0x80410b05,
ERROR_NET_ADHOCCTL_ID_NOT_FOUND = 0x80410B06,
ERROR_NET_ADHOCCTL_ALREADY_INITIALIZED = 0x80410b07,
ERROR_NET_ADHOCCTL_NOT_INITIALIZED = 0x80410b08,
ERROR_NET_ADHOCCTL_DISCONNECTED = 0x80410b09,
ERROR_NET_ADHOCCTL_NO_SCAN_INFO = 0x80410b0a,
ERROR_NET_ADHOCCTL_INVALID_IBSS = 0x80410b0b,
ERROR_NET_ADHOCCTL_NOT_ENTER_GAMEMODE = 0x80410B0C,
ERROR_NET_ADHOCCTL_CHANNEL_NOT_AVAILABLE = 0x80410B0D,
ERROR_NET_ADHOCCTL_WLAN_BEACON_LOST = 0x80410b0e,
ERROR_NET_ADHOCCTL_WLAN_SUSPENDED = 0x80410b0f,
ERROR_NET_ADHOCCTL_BUSY = 0x80410b10,
ERROR_NET_ADHOCCTL_CHANNEL_NOT_MATCH = 0x80410b11,
ERROR_NET_ADHOCCTL_TOO_MANY_HANDLERS = 0x80410b12,
ERROR_NET_ADHOCCTL_STACKSIZE_TOO_SHORT = 0x80410B13,
ERROR_NET_WLAN_INVALID_ARG = 0x80410D13,
ERROR_NET_NO_SPACE = 0x80410001
};
const size_t MAX_ADHOCCTL_HANDLERS = 32; //4
@@ -902,6 +924,7 @@ extern bool friendFinderRunning;
extern SceNetAdhocctlPeerInfo * friends;
extern SceNetAdhocctlScanInfo * networks;
extern u64 adhocctlStartTime;
extern bool isAdhocctlBusy;
extern int adhocctlState;
extern int adhocctlCurrentMode;
extern int adhocConnectionType;
@@ -1014,7 +1037,7 @@ void changeBlockingMode(int fd, int nonblocking);
* Count Virtual Networks by analyzing the Friend List
* @return Number of Virtual Networks
*/
int countAvailableNetworks();
int countAvailableNetworks(const bool excludeSelf = false);
/*
* Find an existing group in networks
@@ -1268,6 +1291,11 @@ int getSockBufferSize(int sock, int opt);
*/
int setSockBufferSize(int sock, int opt, int size);
/*
* Set TCP Socket Maximum Segment Size (default is 1460 on 1500 MTU)
*/
int setSockMSS(int sock, int size);
/*
* Set Socket TimeOut (opt = SO_SNDTIMEO/SO_RCVTIMEO)
*/
+5 -3
View File
@@ -55,6 +55,7 @@
#include "Common/File/FileUtil.h"
#include "Common/TimeUtil.h"
#include "Core/Util/PortManager.h"
#include "Core/Instance.h"
#include "Core/Core.h"
#include "Core/Host.h"
#include "Core/HLE/proAdhocServer.h"
@@ -1823,6 +1824,9 @@ int create_listen_socket(uint16_t port)
// Created Socket
if(fd != -1)
{
// Ignore SIGPIPE when supported (ie. BSD/MacOS)
setSockNoSIGPIPE(fd, 1);
// Enable KeepAlive
enable_keepalive(fd);
@@ -1842,12 +1846,10 @@ int create_listen_socket(uint16_t port)
local.sin_addr.s_addr = INADDR_ANY;
local.sin_port = htons(port);
//Should only bind to specific IP for the 2nd or more instance of PPSSPP to prevent communication interference issue when sharing the same port. Doesn't work well when PPSSPP_ID reseted everytime emulation restarted.
/*
// Should only bind to specific IP for the 2nd or more instance of PPSSPP to prevent communication interference issue when sharing the same port. (ie. Capcom Classics Collection Remixed)
if (PPSSPP_ID > 1) {
local.sin_addr = g_localhostIP.in.sin_addr;
}
*/
// Bind Local Address to Socket
int bindresult = bind(fd, (struct sockaddr *)&local, sizeof(local));
+6 -4
View File
@@ -670,11 +670,12 @@ static void DoFrameIdleTiming() {
// Give a little extra wiggle room in case the next vblank does more work.
const double goal = lastFrameTime + (numVBlanksSinceFlip - 1) * scaledVblank - 0.001;
if (numVBlanksSinceFlip >= 2 && before < goal) {
while (time_now_d() < goal) {
double cur_time;
while ((cur_time = time_now_d()) < goal) {
#ifdef _WIN32
sleep_ms(1);
#else
const double left = goal - time_now_d();
const double left = goal - cur_time;
usleep((long)(left * 1000000));
#endif
}
@@ -893,9 +894,10 @@ void hleLagSync(u64 userdata, int cyclesLate) {
// Don't lag too long ever, if they leave it paused.
double now = before;
while (now < goal && goal < now + 0.01) {
// Tight loop on win32 - intentionally, as timing is otherwise not precise enough.
#ifndef _WIN32
const double left = goal - time_now_d();
usleep((long)(left * 1000000));
const double left = goal - now;
usleep((long)(left * 1000000.0));
#endif
now = time_now_d();
}
+1 -2
View File
@@ -621,8 +621,6 @@ static void __IoVblank() {
}
void __IoInit() {
MemoryStick_Init();
asyncNotifyEvent = CoreTiming::RegisterEvent("IoAsyncNotify", __IoAsyncNotify);
syncNotifyEvent = CoreTiming::RegisterEvent("IoSyncNotify", __IoSyncNotify);
@@ -663,6 +661,7 @@ void __IoInit() {
__KernelRegisterWaitTypeFuncs(WAITTYPE_ASYNCIO, __IoAsyncBeginCallback, __IoAsyncEndCallback);
MemoryStick_Init();
lastMemStickState = MemoryStick_State();
lastMemStickFatState = MemoryStick_FatState();
__DisplayListenVblank(__IoVblank);
+10
View File
@@ -1141,6 +1141,16 @@ static int sceKernelPrintf(const char *formatString)
supported = false;
}
// Scrub for beeps and other suspicious control characters.
for (size_t i = 0; i < result.size(); i++) {
switch (result[i]) {
case 7: // BEL
case 8: // Backspace
result[i] = ' ';
break;
}
}
// Just in case there were embedded strings that had \n's.
if (!result.empty() && result[result.size() - 1] == '\n')
result.resize(result.size() - 1);
+79 -19
View File
@@ -168,6 +168,7 @@ struct NativeModule {
char name[28];
u32_le status;
u32_le unk1;
u32_le modid; // 0x2C
u32_le usermod_thid;
u32_le memid;
u32_le mpidtext;
@@ -233,7 +234,6 @@ enum NativeModuleStatus {
class PSPModule : public KernelObject {
public:
PSPModule() : textStart(0), textEnd(0), libstub(0), libstubend(0), memoryBlockAddr(0), isFake(false) {}
~PSPModule() {
if (memoryBlockAddr) {
// If it's either below user memory, or using a high kernel bit, it's in kernel.
@@ -244,6 +244,11 @@ public:
}
g_symbolMap->UnloadModule(memoryBlockAddr, memoryBlockSize);
}
if (modulePtr) {
//Only alloc at kernel memory.
kernelMemory.Free(modulePtr);
}
}
const char *GetName() override { return nm.name; }
const char *GetTypeName() override { return GetStaticTypeName(); }
@@ -263,11 +268,22 @@ public:
void DoState(PointerWrap &p) override
{
auto s = p.Section("Module", 1, 4);
auto s = p.Section("Module", 1, 5);
if (!s)
return;
Do(p, nm);
if (s >= 5) {
Do(p, nm);
} else {
char temp[192];
NativeModule *pnm = &nm;
char *ptemp = temp;
DoArray(p, ptemp, 0xC0);
memcpy(pnm, ptemp, 0x2C);
pnm->modid = GetUID();
memcpy(((uint8_t *)pnm) + 0x30, ((uint8_t *)ptemp) + 0x2C, 0xC0 - 0x2C);
}
Do(p, memoryBlockAddr);
Do(p, memoryBlockSize);
Do(p, isFake);
@@ -290,6 +306,10 @@ public:
Do(p, libstubend);
}
if (s >= 5) {
Do(p, modulePtr);
}
ModuleWaitingThread mwt = {0};
Do(p, waitingThreads, mwt);
FuncSymbolExport fsx = {{0}};
@@ -402,7 +422,7 @@ public:
return impExpModuleNames.find(moduleName) != impExpModuleNames.end();
}
NativeModule nm;
NativeModule nm{};
std::vector<ModuleWaitingThread> waitingThreads;
std::vector<FuncSymbolExport> exportedFuncs;
@@ -410,19 +430,19 @@ public:
std::vector<VarSymbolExport> exportedVars;
std::vector<VarSymbolImport> importedVars;
std::set<std::string> impExpModuleNames;
// Keep track of the code region so we can throw out analysis results
// when unloaded.
u32 textStart;
u32 textEnd;
u32 textStart = 0;
u32 textEnd = 0;
// Keep track of the libstub pointers so we can recheck on load state.
u32 libstub;
u32 libstubend;
u32 libstub = 0;
u32 libstubend = 0;
u32 memoryBlockAddr;
u32 memoryBlockSize;
bool isFake;
u32 memoryBlockAddr = 0;
u32 memoryBlockSize = 0;
u32 modulePtr = 0;
bool isFake = false;
};
KernelObject *__KernelModuleObject()
@@ -1104,6 +1124,8 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load
loadedModules.insert(module->GetUID());
memset(&module->nm, 0, sizeof(module->nm));
module->nm.modid = module->GetUID();
bool reportedModule = false;
u32 devkitVersion = 0;
u8 *newptr = 0;
@@ -1310,6 +1332,12 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load
if (textSection == -1) {
module->textStart = reader.GetVaddr();
module->textEnd = firstImportStubAddr - 4;
// Reference Jpcsp.
if (reader.GetFirstSegmentAlign() > 0)
module->textStart &= ~(reader.GetFirstSegmentAlign() - 1);
// PSP set these values even if no section.
module->nm.text_addr = module->textStart;
module->nm.text_size = reader.GetTotalTextSizeFromSeg();
}
if (!module->isFake) {
@@ -1540,6 +1568,15 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load
}
}
u32 moduleSize = sizeof(module->nm);
char tag[32];
snprintf(tag, sizeof(tag), "SceModule-%d", module->nm.modid);
module->modulePtr = kernelMemory.Alloc(moduleSize, true, tag);
// Fill the struct.
if (Memory::IsValidAddress(module->modulePtr))
Memory::WriteStruct(module->modulePtr, &module->nm);
error = 0;
return module;
}
@@ -1896,6 +1933,15 @@ u32 sceKernelLoadModule(const char *name, u32 flags, u32 optionAddr) {
module->nm.entry_addr = -1;
module->nm.gp_value = -1;
u32 moduleSize = sizeof(module->nm);
char tag[32];
snprintf(tag, sizeof(tag), "SceModule-%d", module->nm.modid);
module->modulePtr = kernelMemory.Alloc(moduleSize, true, tag);
// Fill the struct.
if(Memory::IsValidAddress(module->modulePtr))
Memory::WriteStruct(module->modulePtr, &module->nm);
// TODO: It would be more ideal to allocate memory for this module.
return hleLogSuccessInfoI(LOADER, module->GetUID(), "created fake module");
@@ -2313,7 +2359,7 @@ void __KernelReturnFromModuleFunc()
{
if (module->nm.status == MODULE_STATUS_UNLOADING) {
// TODO: Maybe should maintain the exitCode?
sceKernelDeleteThread(it->threadID);
sceKernelTerminateDeleteThread(it->threadID);
} else {
if (it->statusPtr != 0)
Memory::Write_U32(exitStatus, it->statusPtr);
@@ -2367,16 +2413,30 @@ static u32 sceKernelGetModuleId()
u32 sceKernelFindModuleByUID(u32 uid)
{
ERROR_LOG(SCEMODULE, "UNIMPL sceKernelFindModuleByUID(%d)", uid);
return 0;
u32 error;
PSPModule *module = kernelObjects.Get<PSPModule>(uid, error);
if (!module || module->isFake) {
ERROR_LOG(SCEMODULE, "0 = sceKernelFindModuleByUID(%d): Module Not Found or Fake", uid);
return 0;
}
INFO_LOG(SCEMODULE, "%d = sceKernelFindModuleByUID(%d)", module->modulePtr, uid);
return module->modulePtr;
}
u32 sceKernelFindModuleByName(const char *name)
{
int index = GetModuleIndex(name);
u32 temp = index + 1;
INFO_LOG(SCEMODULE, "%d = sceKernelFindModuleByName(%s)", temp, name);
return temp;
u32 error;
for (SceUID moduleId : loadedModules) {
PSPModule *module = kernelObjects.Get<PSPModule>(moduleId, error);
if (!module)
continue;
if (!module->isFake && strcmp(name, module->nm.name) == 0) {
INFO_LOG(SCEMODULE, "%d = sceKernelFindModuleByName(%s)", module->modulePtr, name);
return module->modulePtr;
}
}
WARN_LOG(SCEMODULE, "0 = sceKernelFindModuleByName(%s): Module Not Found or Fake", name);
return 0;
}
static u32 sceKernelLoadModuleByID(u32 id, u32 flags, u32 lmoptionPtr)
+9
View File
@@ -32,6 +32,7 @@
#include "Core/HLE/sceKernelThread.h"
#include "Core/HLE/sceRtc.h"
#include "Core/MemMap.h"
#include "Core/System.h"
#include "StringUtils.h"
// The time when the game started.
@@ -40,6 +41,14 @@ static time_t start_time;
void __KernelTimeInit()
{
time(&start_time);
if (PSP_CoreParameter().compat.flags().DateLimited) {
// Car Jack Streets(NPUZ00043) requires that the date cannot exceed a certain time.
// 2011 year makes it work fine.
tm *tm;
tm = localtime(&start_time);
tm->tm_year = 111;// 2011 year.
start_time = mktime(tm);
}
}
void __KernelTimeDoState(PointerWrap &p)
+51 -30
View File
@@ -98,6 +98,10 @@ static std::list<AVFrame*> pmp_queue; //list of pmp video frames have been decod
static std::list<u32> pmp_ContextList; //list of pmp media contexts
static bool pmp_oldStateLoaded = false; // for dostate
// Calculate the number of total packets added to the ringbuffer by calling the sceMpegRingbufferPut() once.
static int ringbufferPutPacketsAdded = 0;
static bool useRingbufferPutCallbackMulti = true;
#ifdef USE_FFMPEG
extern "C" {
@@ -387,7 +391,7 @@ void __MpegInit() {
}
void __MpegDoState(PointerWrap &p) {
auto s = p.Section("sceMpeg", 1, 2);
auto s = p.Section("sceMpeg", 1, 3);
if (!s)
return;
@@ -400,6 +404,12 @@ void __MpegDoState(PointerWrap &p) {
// Let's assume the oldest version.
mpegLibVersion = 0x0101;
} else {
if (s < 3) {
useRingbufferPutCallbackMulti = false;
ringbufferPutPacketsAdded = 0;
} else {
Do(p, ringbufferPutPacketsAdded);
}
Do(p, streamIdGen);
Do(p, mpegLibVersion);
}
@@ -667,6 +677,7 @@ static int sceMpegRegistStream(u32 mpeg, u32 streamType, u32 streamNum)
switch (streamType) {
case MPEG_AVC_STREAM:
ctx->avcRegistered = true;
ctx->mediaengine->addVideoStream(streamNum);
// TODO: Probably incorrect?
ctx->mediaengine->setVideoStream(streamNum);
break;
@@ -1437,16 +1448,17 @@ void PostPutAction::run(MipsCall &call) {
int writeOffset = ringbuffer->packetsWritePos % (s32)ringbuffer->packets;
const u8 *data = Memory::GetPointer(ringbuffer->data + writeOffset * 2048);
int packetsAdded = currentMIPS->r[MIPS_REG_V0];
int packetsAddedThisRound = currentMIPS->r[MIPS_REG_V0];
ringbufferPutPacketsAdded += packetsAddedThisRound;
// It seems validation is done only by older mpeg libs.
if (mpegLibVersion < 0x0105 && packetsAdded > 0) {
if (mpegLibVersion < 0x0105 && packetsAddedThisRound > 0) {
// TODO: Faster / less wasteful validation.
std::unique_ptr<MpegDemux> demuxer(new MpegDemux(packetsAdded * 2048, 0));
std::unique_ptr<MpegDemux> demuxer(new MpegDemux(packetsAddedThisRound * 2048, 0));
int readOffset = ringbuffer->packetsRead % (s32)ringbuffer->packets;
const u8 *buf = Memory::GetPointer(ringbuffer->data + readOffset * 2048);
bool invalid = false;
for (int i = 0; i < packetsAdded; ++i) {
for (int i = 0; i < packetsAddedThisRound; ++i) {
demuxer->addStreamData(buf, 2048);
buf += 2048;
@@ -1461,40 +1473,43 @@ void PostPutAction::run(MipsCall &call) {
if (mpegLibVersion <= 0x0103) {
// Act like they were actually added, but don't increment read pos.
ringbuffer->packetsWritePos += packetsAdded;
ringbuffer->packetsAvail += packetsAdded;
ringbuffer->packetsWritePos += packetsAddedThisRound;
ringbuffer->packetsAvail += packetsAddedThisRound;
}
return;
}
}
if (ringbuffer->packetsRead == 0 && ctx->mediaengine && packetsAdded > 0) {
if (ringbuffer->packetsRead == 0 && ctx->mediaengine && packetsAddedThisRound > 0) {
// init mediaEngine
AnalyzeMpeg(ctx->mpegheader, 2048, ctx);
ctx->mediaengine->loadStream(ctx->mpegheader, 2048, ringbuffer->packets * ringbuffer->packetSize);
}
if (packetsAdded > 0) {
if (packetsAdded > ringbuffer->packets - ringbuffer->packetsAvail) {
WARN_LOG(ME, "sceMpegRingbufferPut clamping packetsAdded old=%i new=%i", packetsAdded, ringbuffer->packets - ringbuffer->packetsAvail);
packetsAdded = ringbuffer->packets - ringbuffer->packetsAvail;
if (packetsAddedThisRound > 0) {
if (packetsAddedThisRound > ringbuffer->packets - ringbuffer->packetsAvail) {
WARN_LOG(ME, "sceMpegRingbufferPut clamping packetsAdded old=%i new=%i", packetsAddedThisRound, ringbuffer->packets - ringbuffer->packetsAvail);
packetsAddedThisRound = ringbuffer->packets - ringbuffer->packetsAvail;
}
int actuallyAdded = ctx->mediaengine == NULL ? 8 : ctx->mediaengine->addStreamData(data, packetsAdded * 2048) / 2048;
if (actuallyAdded != packetsAdded) {
int actuallyAdded = ctx->mediaengine == NULL ? 8 : ctx->mediaengine->addStreamData(data, packetsAddedThisRound * 2048) / 2048;
if (actuallyAdded != packetsAddedThisRound) {
WARN_LOG_REPORT(ME, "sceMpegRingbufferPut(): unable to enqueue all added packets, going to overwrite some frames.");
}
ringbuffer->packetsRead += packetsAdded;
ringbuffer->packetsWritePos += packetsAdded;
ringbuffer->packetsAvail += packetsAdded;
ringbuffer->packetsRead += packetsAddedThisRound;
ringbuffer->packetsWritePos += packetsAddedThisRound;
ringbuffer->packetsAvail += packetsAddedThisRound;
}
DEBUG_LOG(ME, "packetAdded: %i packetsRead: %i packetsTotal: %i", packetsAdded, ringbuffer->packetsRead, ringbuffer->packets);
DEBUG_LOG(ME, "packetAdded: %i packetsRead: %i packetsTotal: %i", packetsAddedThisRound, ringbuffer->packetsRead, ringbuffer->packets);
call.setReturnValue(packetsAdded);
call.setReturnValue(ringbufferPutPacketsAdded);
}
// Program signals that it has written data to the ringbuffer and gets a callback ?
static u32 sceMpegRingbufferPut(u32 ringbufferAddr, int numPackets, int available)
{
// Generally, program will call sceMpegRingbufferAvailableSize() before this func.
// Still need to check available?
numPackets = std::min(numPackets, available);
if (numPackets <= 0) {
DEBUG_LOG(ME, "sceMpegRingbufferPut(%08x, %i, %i): no packets to enqueue", ringbufferAddr, numPackets, available);
@@ -1513,20 +1528,29 @@ static u32 sceMpegRingbufferPut(u32 ringbufferAddr, int numPackets, int availabl
WARN_LOG(ME, "sceMpegRingbufferPut(%08x, %i, %i): bad mpeg handle %08x", ringbufferAddr, numPackets, available, ringbuffer->mpeg);
return -1;
}
ringbufferPutPacketsAdded = 0;
// Execute callback function as a direct MipsCall, no blocking here so no messing around with wait states etc
if (ringbuffer->callback_addr != 0) {
DEBUG_LOG(ME, "sceMpegRingbufferPut(%08x, %i, %i)", ringbufferAddr, numPackets, available);
PostPutAction *action = (PostPutAction *)__KernelCreateAction(actionPostPut);
action->setRingAddr(ringbufferAddr);
// TODO: Should call this multiple times until we get numPackets.
// Call this multiple times until we get numPackets.
// Normally this would be if it did not read enough, but also if available > packets.
// Should ultimately return the TOTAL number of returned packets.
int writeOffset = ringbuffer->packetsWritePos % (s32)ringbuffer->packets;
u32 packetsThisRound = std::min(numPackets, (s32)ringbuffer->packets - writeOffset);
u32 args[3] = {(u32)ringbuffer->data + (u32)writeOffset * 2048, packetsThisRound, (u32)ringbuffer->callback_args};
hleEnqueueCall(ringbuffer->callback_addr, 3, args, action);
u32 packetsThisRound = 0;
while (numPackets) {
PostPutAction *action = (PostPutAction *)__KernelCreateAction(actionPostPut);
action->setRingAddr(ringbufferAddr);
packetsThisRound = std::min(numPackets, (s32)ringbuffer->packets - writeOffset);
numPackets -= packetsThisRound;
u32 args[3] = { (u32)ringbuffer->data + (u32)writeOffset * 2048, packetsThisRound, (u32)ringbuffer->callback_args };
hleEnqueueCall(ringbuffer->callback_addr, 3, args, action);
writeOffset = (writeOffset + packetsThisRound) % (s32)ringbuffer->packets;
// Old savestate don't use this feature, just for compatibility.
if (!useRingbufferPutCallbackMulti)
break;
}
} else {
ERROR_LOG_REPORT(ME, "sceMpegRingbufferPut: callback_addr zero");
}
@@ -1953,10 +1977,7 @@ static u32 sceMpegAvcCsc(u32 mpeg, u32 sourceAddr, u32 rangeAddr, int frameWidth
int y = Memory::Read_U32(rangeAddr + 4);
int width = Memory::Read_U32(rangeAddr + 8);
int height = Memory::Read_U32(rangeAddr + 12);
if (((x | y | width | height) & 0xF) != 0) {
WARN_LOG(ME, "sceMpegAvcCsc(%08x, %08x, %08x, %i, %08x) returning ERROR_MPEG_INVALID_VALUE", mpeg, sourceAddr, rangeAddr, frameWidth, destAddr);
return ERROR_MPEG_INVALID_VALUE;
}
if (x < 0 || y < 0 || width < 0 || height < 0) {
WARN_LOG(ME, "sceMpegAvcCsc(%08x, %08x, %08x, %i, %08x) returning ERROR_INVALID_VALUE", mpeg, sourceAddr, rangeAddr, frameWidth, destAddr);
return SCE_KERNEL_ERROR_INVALID_VALUE;
+7 -1
View File
@@ -135,8 +135,10 @@ static void __ApctlState(u64 userdata, int cyclesLate) {
u32 error = 0;
SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error);
if (waitID == 0 || error != 0)
if (waitID == 0 || error != 0) {
WARN_LOG(SCENET, "sceNetApctl State WaitID(%i) on Thread(%i) already woken up? (error: %d)", uid, threadID, error);
return;
}
u32 waitVal = __KernelGetWaitValue(threadID, error);
if (error == 0) {
@@ -693,6 +695,10 @@ static u32 sceWlanGetEtherAddr(u32 addrAddr) {
}
static u32 sceNetGetLocalEtherAddr(u32 addrAddr) {
// FIXME: Return 0x80410180 (pspnet[_core] error code?) before successful attempt to Create/Connect/Join a Group? (ie. adhocctlCurrentMode == ADHOCCTL_MODE_NONE)
if (adhocctlCurrentMode == ADHOCCTL_MODE_NONE)
return hleLogDebug(SCENET, 0x80410180, "address not available?");
return sceWlanGetEtherAddr(addrAddr);
}
+93 -3
View File
@@ -49,14 +49,80 @@
const size_t MAX_APCTL_HANDLERS = 32;
enum {
ERROR_NET_BUFFER_TOO_SMALL = 0x80400706,
// net common errors
ERROR_NET_NO_SPACE = 0x80410001,
ERROR_NET_INTERNAL = 0x80410002,
ERROR_NET_INVALID_ARG = 0x80410003,
ERROR_NET_NO_ENTRY = 0x80410004,
// pspnet_core
ERROR_NET_CORE_NOT_TERMINATED = 0x80410101,
ERROR_NET_CORE_INTERFACE_BUSY = 0x80410102,
ERROR_NET_CORE_INVALID_ARG = 0x80410103,
ERROR_NET_CORE_THREAD_NOT_FOUND = 0x80410104,
ERROR_NET_CORE_THREAD_BUSY = 0x80410105,
ERROR_NET_CORE_80211_NO_BSS = 0x80410106,
ERROR_NET_CORE_80211_NO_AVAIL_BSS = 0x80410107,
// pspnet_inet
ERROR_NET_INET_ALREADY_INITIALIZED = 0x80410201,
ERROR_NET_INET_SOCKET_BUSY = 0x80410202,
ERROR_NET_INET_CONFIG_INVALID_ARG = 0x80410203,
ERROR_NET_INET_GET_IFADDR = 0x80410204,
ERROR_NET_INET_SET_IFADDR = 0x80410205,
ERROR_NET_INET_DEL_IFADDR = 0x80410206,
ERROR_NET_INET_NO_DEFAULT_ROUTE = 0x80410207,
ERROR_NET_INET_GET_ROUTE = 0x80410208,
ERROR_NET_INET_SET_ROUTE = 0x80410209,
ERROR_NET_INET_FLUSH_ROUTE = 0x8041020a,
ERROR_NET_INET_INVALID_ARG = 0x8041020b,
ERROR_NET_RESOLVER_BAD_ID = 0x80410408,
// pspnet_poeclient
ERROR_NET_POECLIENT_INIT = 0x80410301,
ERROR_NET_POECLIENT_NO_PADO = 0x80410302,
ERROR_NET_POECLIENT_NO_PADS = 0x80410303,
ERROR_NET_POECLIENT_GET_PADT = 0x80410304,
ERROR_NET_POECLIENT_SERVICE_NAME = 0x80410305,
ERROR_NET_POECLIENT_AC_SYSTEM = 0x80410306,
ERROR_NET_POECLIENT_GENERIC = 0x80410307,
ERROR_NET_POECLIENT_AUTH = 0x80410308,
ERROR_NET_POECLIENT_NETWORK = 0x80410309,
ERROR_NET_POECLIENT_TERMINATE = 0x8041030a,
ERROR_NET_POECLIENT_NOT_STARTED = 0x8041030b,
// pspnet_resolver
ERROR_NET_RESOLVER_NOT_TERMINATED = 0x80410401,
ERROR_NET_RESOLVER_NO_DNS_SERVER = 0x80410402,
ERROR_NET_RESOLVER_INVALID_PTR = 0x80410403,
ERROR_NET_RESOLVER_INVALID_BUFLEN = 0x80410404,
ERROR_NET_RESOLVER_INVALID_ID = 0x80410405,
ERROR_NET_RESOLVER_ID_MAX = 0x80410406,
ERROR_NET_RESOLVER_NO_MEM = 0x80410407,
ERROR_NET_RESOLVER_BAD_ID = 0x80410408, // ERROR_NET_RESOLVER_ID_NOT_FOUND
ERROR_NET_RESOLVER_CTX_BUSY = 0x80410409,
ERROR_NET_RESOLVER_ALREADY_STOPPED = 0x8041040a,
ERROR_NET_RESOLVER_INVALID_HOST = 0x80410414,
ERROR_NET_RESOLVER_NOT_SUPPORTED = 0x8041040b,
ERROR_NET_RESOLVER_BUF_NO_SPACE = 0x8041040c,
ERROR_NET_RESOLVER_INVALID_PACKET = 0x8041040d,
ERROR_NET_RESOLVER_STOPPED = 0x8041040e,
ERROR_NET_RESOLVER_SOCKET = 0x8041040f,
ERROR_NET_RESOLVER_TIMEOUT = 0x80410410,
ERROR_NET_RESOLVER_NO_RECORD = 0x80410411,
ERROR_NET_RESOLVER_RES_PACKET_FORMAT = 0x80410412,
ERROR_NET_RESOLVER_RES_SERVER_FAILURE = 0x80410413,
ERROR_NET_RESOLVER_INVALID_HOST = 0x80410414, // ERROR_NET_RESOLVER_NO_HOST
ERROR_NET_RESOLVER_RES_NOT_IMPLEMENTED = 0x80410415,
ERROR_NET_RESOLVER_RES_SERVER_REFUSED = 0x80410416,
ERROR_NET_RESOLVER_INTERNAL = 0x80410417,
// pspnet_dhcp
ERROR_NET_DHCP_INVALID_PACKET = 0x80410501,
ERROR_NET_DHCP_NO_SERVER = 0x80410502,
ERROR_NET_DHCP_SENT_DECLINE = 0x80410503,
ERROR_NET_DHCP_LEASE_TIME = 0x80410504,
ERROR_NET_DHCP_GET_NAK = 0x80410505,
// pspnet_apctl
ERROR_NET_APCTL_ALREADY_INITIALIZED = 0x80410a01,
ERROR_NET_APCTL_INVALID_CODE = 0x80410a02,
ERROR_NET_APCTL_INVALID_IP = 0x80410a03,
@@ -68,6 +134,30 @@ enum {
ERROR_NET_APCTL_INVALID_ID = 0x80410a09,
ERROR_NET_APCTL_WLAN_SUSPENDED = 0x80410a0a,
ERROR_NET_APCTL_TIMEOUT = 0x80410a0b,
// wlan errors
ERROR_NET_WLAN_ALREADY_JOINED = 0x80410d01,
ERROR_NET_WLAN_TRY_JOIN = 0x80410d02,
ERROR_NET_WLAN_SCANNING = 0x80410d03,
ERROR_NET_WLAN_INVALID_PARAMETER = 0x80410d04,
ERROR_NET_WLAN_NOT_SUPPORTED = 0x80410d05,
ERROR_NET_WLAN_NOT_JOIN_BSS = 0x80410d06,
ERROR_NET_WLAN_ASSOC_TIMEOUT = 0x80410d07,
ERROR_NET_WLAN_ASSOC_REFUSED = 0x80410d08,
ERROR_NET_WLAN_ASSOC_FAIL = 0x80410d09,
ERROR_NET_WLAN_DISASSOC_FAIL = 0x80410d0a,
ERROR_NET_WLAN_JOIN_FAIL = 0x80410d0b,
ERROR_NET_WLAN_POWER_OFF = 0x80410d0c,
ERROR_NET_WLAN_INTERNAL_FAIL = 0x80410d0d,
ERROR_NET_WLAN_DEVICE_NOT_READY = 0x80410d0e,
ERROR_NET_WLAN_ALREADY_ATTACHED = 0x80410d0f,
ERROR_NET_WLAN_NOT_SET_WEP = 0x80410d10,
ERROR_NET_WLAN_TIMEOUT = 0x80410d11,
ERROR_NET_WLAN_NO_SPACE = 0x80410d12,
ERROR_NET_WLAN_INVALID_ARG = 0x80410D13,
ERROR_NET_WLAN_NOT_IN_GAMEMODE = 0x80410d14,
ERROR_NET_WLAN_LEAVE_FAIL = 0x80410d15,
ERROR_NET_WLAN_SUSPENDED = 0x80410d16,
};
enum {
+743 -474
View File
File diff suppressed because it is too large. Load diff
+2
View File
@@ -98,6 +98,8 @@ int NetAdhoc_Term();
extern bool netAdhocInited;
extern bool netAdhocctlInited;
extern bool networkInited;
extern bool netAdhocGameModeEntered;
extern int netAdhocEnterGameModeTimeout;
extern int adhocDefaultTimeout; //3000000 usec
extern int adhocDefaultDelay; //10000
extern int adhocExtraDelay; //20000
+70 -99
View File
@@ -29,6 +29,7 @@
#include "Core/HLE/sceMpeg.h"
#include "Core/HLE/sceKernelMemory.h"
#include "Core/HW/MediaEngine.h"
#include "Core/CoreTiming.h"
#include "GPU/GPUInterface.h"
#include "GPU/GPUState.h"
@@ -59,6 +60,8 @@ static int videoPixelMode = GE_CMODE_32BIT_ABGR8888;
static int videoLoopStatus = PSMF_PLAYER_CONFIG_NO_LOOP;
static int psmfPlayerLibVersion = 0;
int eventPsmfPlayerStatusChange = -1;
enum PsmfPlayerError {
ERROR_PSMF_NOT_INITIALIZED = 0x80615001,
ERROR_PSMF_BAD_VERSION = 0x80615002,
@@ -666,10 +669,20 @@ static PsmfPlayer *getPsmfPlayer(u32 psmfplayer)
return 0;
}
static void __PsmfPlayerStatusChange(u64 userdata, int cyclesLate) {
PsmfPlayerStatus status = PsmfPlayerStatus(userdata & 0xFFFFFFFF);
u32 psmfPlayer = userdata >> 32;
PsmfPlayer *player = getPsmfPlayer(psmfPlayer);
if (player) {
player->status = status;
}
}
void __PsmfInit() {
videoPixelMode = GE_CMODE_32BIT_ABGR8888;
videoLoopStatus = PSMF_PLAYER_CONFIG_NO_LOOP;
psmfPlayerLibVersion = 0;
eventPsmfPlayerStatusChange = CoreTiming::RegisterEvent("PsmfPlayerStatusChange", &__PsmfPlayerStatusChange);
}
void __PsmfPlayerLoadModule(int devkitVersion) {
@@ -685,18 +698,26 @@ void __PsmfDoState(PointerWrap &p) {
}
void __PsmfPlayerDoState(PointerWrap &p) {
auto s = p.Section("scePsmfPlayer", 1, 2);
auto s = p.Section("scePsmfPlayer", 1, 3);
if (!s)
return;
Do(p, psmfPlayerMap);
Do(p, videoPixelMode);
Do(p, videoLoopStatus);
if (s >= 2) {
Do(p, psmfPlayerLibVersion);
if (s < 3) {
eventPsmfPlayerStatusChange = -1;
} else {
Do(p, eventPsmfPlayerStatusChange);
if (eventPsmfPlayerStatusChange != -1) {
CoreTiming::RestoreRegisterEvent(eventPsmfPlayerStatusChange, "PsmfPlayerStatusChangeEvent", &__PsmfPlayerStatusChange);
}
}
if (s < 2) {
// Assume the latest, which is what we were emulating before.
psmfPlayerLibVersion = 0x06060010;
} else {
Do(p, psmfPlayerLibVersion);
}
}
@@ -709,6 +730,13 @@ void __PsmfShutdown() {
psmfPlayerMap.clear();
}
static void DelayPsmfStateChange(u32 psmfPlayer, u32 newState, s64 delayUs) {
if (eventPsmfPlayerStatusChange == -1) {
eventPsmfPlayerStatusChange = CoreTiming::RegisterEvent("PsmfPlayerStatusChange", &__PsmfPlayerStatusChange);
}
CoreTiming::ScheduleEvent(usToCycles(delayUs), eventPsmfPlayerStatusChange, (u64)psmfPlayer << 32 | newState);
}
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
if (!Memory::IsValidAddress(psmfData) || !Memory::IsValidAddress(psmfData)) {
// Crashes on a PSP.
@@ -1082,38 +1110,32 @@ static u32 scePsmfGetEPidWithTimestamp(u32 psmfStruct, u32 ts)
return epid;
}
static int scePsmfPlayerCreate(u32 psmfPlayer, u32 dataPtr)
{
static int scePsmfPlayerCreate(u32 psmfPlayer, u32 dataPtr) {
auto player = PSPPointer<u32>::Create(psmfPlayer);
const auto data = PSPPointer<const PsmfPlayerCreateData>::Create(dataPtr);
if (!player.IsValid() || !data.IsValid()) {
ERROR_LOG_REPORT(ME, "scePsmfPlayerCreate(%08x, %08x): bad pointers", psmfPlayer, dataPtr);
// Crashes on a PSP.
return SCE_KERNEL_ERROR_ILLEGAL_ADDRESS;
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad pointers");
}
if (!data->buffer.IsValid()) {
ERROR_LOG_REPORT(ME, "scePsmfPlayerCreate(%08x, %08x): invalid buffer address %08x", psmfPlayer, dataPtr, data->buffer.ptr);
// Also crashes on a PSP.
*player = 0;
return SCE_KERNEL_ERROR_ILLEGAL_ADDRESS;
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "invalid buffer address %08x", data->buffer.ptr);
}
if (data->bufferSize < 0x00285800) {
ERROR_LOG_REPORT(ME, "scePsmfPlayerCreate(%08x, %08x): buffer too small %08x", psmfPlayer, dataPtr, data->bufferSize);
*player = 0;
return ERROR_PSMFPLAYER_BUFFER_SIZE;
return hleReportError(ME, ERROR_PSMFPLAYER_BUFFER_SIZE, "buffer too small %08x", data->bufferSize);
}
if (data->threadPriority < 0x10 || data->threadPriority >= 0x6E) {
ERROR_LOG_REPORT(ME, "scePsmfPlayerCreate(%08x, %08x): bad thread priority %02x", psmfPlayer, dataPtr, data->threadPriority);
*player = 0;
return ERROR_PSMFPLAYER_INVALID_PARAM;
return hleReportError(ME, ERROR_PSMFPLAYER_INVALID_PARAM, "bad thread priority %02x", data->threadPriority);
}
if (!psmfPlayerMap.empty()) {
*player = 0;
return hleReportError(ME, ERROR_MPEG_ALREADY_INIT, "already have an active player");
}
INFO_LOG(ME, "scePsmfPlayerCreate(%08x, %08x)", psmfPlayer, dataPtr);
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
if (!psmfplayer) {
psmfplayer = new PsmfPlayer(data);
@@ -1129,39 +1151,35 @@ static int scePsmfPlayerCreate(u32 psmfPlayer, u32 dataPtr)
videoPixelMode = GE_CMODE_32BIT_ABGR8888;
videoLoopStatus = PSMF_PLAYER_CONFIG_NO_LOOP;
psmfplayer->status = PSMF_PLAYER_STATUS_INIT;
return hleDelayResult(0, "player create", 20000);
int delayUs = 20000;
DelayPsmfStateChange(psmfPlayer, PSMF_PLAYER_STATUS_INIT, delayUs);
return hleLogSuccessInfoI(ME, hleDelayResult(0, "player create", delayUs));
}
static int scePsmfPlayerStop(u32 psmfPlayer) {
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
if (!psmfplayer) {
ERROR_LOG(ME, "scePsmfPlayerStop(%08x): invalid psmf player", psmfPlayer);
return ERROR_PSMFPLAYER_INVALID_STATUS;
return hleLogError(ME, ERROR_PSMFPLAYER_INVALID_STATUS, "invalid psmf player");
}
if (psmfplayer->status < PSMF_PLAYER_STATUS_PLAYING) {
ERROR_LOG(ME, "scePsmfPlayerStop(%08x): not yet playing", psmfPlayer);
return ERROR_PSMFPLAYER_INVALID_STATUS;
return hleLogError(ME, ERROR_PSMFPLAYER_INVALID_STATUS, "not yet playing");
}
psmfplayer->AbortFinish();
INFO_LOG(ME, "scePsmfPlayerStop(%08x)", psmfPlayer);
psmfplayer->status = PSMF_PLAYER_STATUS_STANDBY;
return hleDelayResult(0, "psmfplayer stop", 3000);
int delayUs = 3000;
DelayPsmfStateChange(psmfPlayer, PSMF_PLAYER_STATUS_STANDBY, delayUs);
return hleLogSuccessInfoI(ME, hleDelayResult(0, "psmfplayer stop", delayUs));
}
static int scePsmfPlayerBreak(u32 psmfPlayer)
{
WARN_LOG(ME, "scePsmfPlayerBreak(%08x)", psmfPlayer);
static int scePsmfPlayerBreak(u32 psmfPlayer) {
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
if (!psmfplayer) {
ERROR_LOG(ME, "scePsmfPlayerBreak(%08x): invalid psmf player", psmfPlayer);
return ERROR_PSMFPLAYER_INVALID_STATUS;
return hleLogError(ME, ERROR_PSMFPLAYER_INVALID_STATUS, "invalid psmf player", psmfPlayer);
}
psmfplayer->AbortFinish();
return 0;
return hleLogWarning(ME, 0);
}
static int _PsmfPlayerFillRingbuffer(PsmfPlayer *psmfplayer) {
@@ -1200,17 +1218,17 @@ static int _PsmfPlayerFillRingbuffer(PsmfPlayer *psmfplayer) {
static int _PsmfPlayerSetPsmfOffset(u32 psmfPlayer, const char *filename, int offset, bool docallback) {
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
if (!psmfplayer || psmfplayer->status != PSMF_PLAYER_STATUS_INIT) {
return ERROR_PSMFPLAYER_INVALID_STATUS;
return hleReportError(ME, ERROR_PSMFPLAYER_INVALID_STATUS, "invalid psmf player or status");
}
if (!filename) {
return ERROR_PSMFPLAYER_INVALID_PARAM;
return hleLogError(ME, ERROR_PSMFPLAYER_INVALID_PARAM, "invalid filename");
}
int delayUs = 1100;
psmfplayer->filehandle = pspFileSystem.OpenFile(filename, (FileAccess) FILEACCESS_READ);
if (psmfplayer->filehandle < 0) {
return hleDelayResult(SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, "psmfplayer set", delayUs);
return hleLogError(ME, hleDelayResult(SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, "psmfplayer set", delayUs), "invalid file data or does not exist");
}
if (offset != 0)
@@ -1223,15 +1241,14 @@ static int _PsmfPlayerSetPsmfOffset(u32 psmfPlayer, const char *filename, int of
const u32 magic = *(u32_le *)buf;
if (magic != PSMF_MAGIC) {
// TODO: Let's keep trying as we were before.
ERROR_LOG_REPORT(ME, "scePsmfPlayerSetPsmf*: incorrect PSMF magic, bad data");
//return SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT;
ERROR_LOG_REPORT(ME, "scePsmfPlayerSetPsmf*: incorrect PSMF magic (%08x), bad data", magic);
//return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, "incorrect PSMF magic (%08x), bad data", magic);
}
// TODO: Merge better with Psmf.
u16 numStreams = *(u16_be *)(buf + 0x80);
if (numStreams > 128) {
ERROR_LOG_REPORT(ME, "scePsmfPlayerSetPsmf*: too many streams in PSMF video, bogus data");
return hleDelayResult(SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, "psmfplayer set", delayUs);
return hleReportError(ME, hleDelayResult(SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, "psmfplayer set", delayUs), "too many streams in PSMF video, bogus data");
}
psmfplayer->totalVideoStreams = 0;
@@ -1274,83 +1291,34 @@ static int _PsmfPlayerSetPsmfOffset(u32 psmfPlayer, const char *filename, int of
_PsmfPlayerFillRingbuffer(psmfplayer);
psmfplayer->totalDurationTimestamp = psmfplayer->mediaengine->getLastTimeStamp();
psmfplayer->status = PSMF_PLAYER_STATUS_STANDBY;
return hleDelayResult(0, "psmfplayer set", delayUs);
DelayPsmfStateChange(psmfPlayer, PSMF_PLAYER_STATUS_STANDBY, delayUs);
return hleLogSuccessInfoI(ME, hleDelayResult(0, "psmfplayer set", delayUs));
}
static int scePsmfPlayerSetPsmf(u32 psmfPlayer, const char *filename)
{
u32 result = _PsmfPlayerSetPsmfOffset(psmfPlayer, filename, 0, false);
if (result == ERROR_PSMFPLAYER_INVALID_STATUS) {
ERROR_LOG_REPORT(ME, "scePsmfPlayerSetPsmf(%08x, %s): invalid psmf player or status", psmfPlayer, filename);
} else if (result == ERROR_PSMFPLAYER_INVALID_PARAM) {
ERROR_LOG(ME, "scePsmfPlayerSetPsmf(%08x, %s): invalid filename", psmfPlayer, filename);
} else if (result == SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT) {
ERROR_LOG(ME, "scePsmfPlayerSetPsmf(%08x, %s): invalid file data or does not exist", psmfPlayer, filename);
} else {
INFO_LOG(ME, "scePsmfPlayerSetPsmf(%08x, %s)", psmfPlayer, filename);
}
return result;
static int scePsmfPlayerSetPsmf(u32 psmfPlayer, const char *filename) {
return _PsmfPlayerSetPsmfOffset(psmfPlayer, filename, 0, false);
}
static int scePsmfPlayerSetPsmfCB(u32 psmfPlayer, const char *filename)
{
static int scePsmfPlayerSetPsmfCB(u32 psmfPlayer, const char *filename) {
// TODO: hleCheckCurrentCallbacks?
u32 result = _PsmfPlayerSetPsmfOffset(psmfPlayer, filename, 0, true);
if (result == ERROR_PSMFPLAYER_INVALID_STATUS) {
ERROR_LOG_REPORT(ME, "scePsmfPlayerSetPsmfCB(%08x, %s): invalid psmf player or status", psmfPlayer, filename);
} else if (result == ERROR_PSMFPLAYER_INVALID_PARAM) {
ERROR_LOG(ME, "scePsmfPlayerSetPsmfCB(%08x, %s): invalid filename", psmfPlayer, filename);
} else if (result == SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT) {
ERROR_LOG(ME, "scePsmfPlayerSetPsmfCB(%08x, %s): invalid file data or does not exist", psmfPlayer, filename);
} else {
INFO_LOG(ME, "scePsmfPlayerSetPsmfCB(%08x, %s)", psmfPlayer, filename);
}
return result;
return _PsmfPlayerSetPsmfOffset(psmfPlayer, filename, 0, true);
}
static int scePsmfPlayerSetPsmfOffset(u32 psmfPlayer, const char *filename, int offset)
{
u32 result = _PsmfPlayerSetPsmfOffset(psmfPlayer, filename, offset, false);
if (result == ERROR_PSMFPLAYER_INVALID_STATUS) {
ERROR_LOG_REPORT(ME, "scePsmfPlayerSetPsmfOffset(%08x, %s): invalid psmf player or status", psmfPlayer, filename);
} else if (result == ERROR_PSMFPLAYER_INVALID_PARAM) {
ERROR_LOG(ME, "scePsmfPlayerSetPsmfOffset(%08x, %s): invalid filename", psmfPlayer, filename);
} else if (result == SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT) {
ERROR_LOG(ME, "scePsmfPlayerSetPsmfOffset(%08x, %s): invalid file data or does not exist", psmfPlayer, filename);
} else {
INFO_LOG(ME, "scePsmfPlayerSetPsmfOffset(%08x, %s)", psmfPlayer, filename);
}
return result;
static int scePsmfPlayerSetPsmfOffset(u32 psmfPlayer, const char *filename, int offset) {
return _PsmfPlayerSetPsmfOffset(psmfPlayer, filename, offset, false);
}
static int scePsmfPlayerSetPsmfOffsetCB(u32 psmfPlayer, const char *filename, int offset)
{
static int scePsmfPlayerSetPsmfOffsetCB(u32 psmfPlayer, const char *filename, int offset) {
// TODO: hleCheckCurrentCallbacks?
u32 result = _PsmfPlayerSetPsmfOffset(psmfPlayer, filename, offset, true);
if (result == ERROR_PSMFPLAYER_INVALID_STATUS) {
ERROR_LOG_REPORT(ME, "scePsmfPlayerSetPsmfOffsetCB(%08x, %s): invalid psmf player or status", psmfPlayer, filename);
} else if (result == ERROR_PSMFPLAYER_INVALID_PARAM) {
ERROR_LOG(ME, "scePsmfPlayerSetPsmfOffsetCB(%08x, %s): invalid filename", psmfPlayer, filename);
} else if (result == SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT) {
ERROR_LOG(ME, "scePsmfPlayerSetPsmfOffsetCB(%08x, %s): invalid file data or does not exist", psmfPlayer, filename);
} else {
INFO_LOG(ME, "scePsmfPlayerSetPsmfOffsetCB(%08x, %s)", psmfPlayer, filename);
}
return result;
return _PsmfPlayerSetPsmfOffset(psmfPlayer, filename, offset, true);
}
static int scePsmfPlayerGetAudioOutSize(u32 psmfPlayer)
{
static int scePsmfPlayerGetAudioOutSize(u32 psmfPlayer) {
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
if (!psmfplayer) {
ERROR_LOG(ME, "scePsmfPlayerGetAudioOutSize(%08x): invalid psmf player", psmfPlayer);
return ERROR_PSMFPLAYER_INVALID_STATUS;
return hleLogError(ME, ERROR_PSMFPLAYER_INVALID_STATUS, "invalid psmf player");
}
WARN_LOG(ME, "%i = scePsmfPlayerGetAudioOutSize(%08x)", audioSamplesBytes, psmfPlayer);
return audioSamplesBytes;
return hleLogWarning(ME, audioSamplesBytes);
}
static bool __PsmfPlayerContinueSeek(PsmfPlayer *psmfplayer, int tries = 50) {
@@ -1460,7 +1428,10 @@ static int scePsmfPlayerStart(u32 psmfPlayer, u32 psmfPlayerData, int initPts)
// Does not alter current pts, it just catches up when Update()/etc. get there.
int delayUs = psmfplayer->status == PSMF_PLAYER_STATUS_PLAYING ? 3000 : 0;
psmfplayer->status = PSMF_PLAYER_STATUS_PLAYING;
if (delayUs == 0)
psmfplayer->status = PSMF_PLAYER_STATUS_PLAYING;
else
DelayPsmfStateChange(psmfPlayer, PSMF_PLAYER_STATUS_PLAYING, delayUs);
psmfplayer->warmUp = 0;
psmfplayer->mediaengine->openContext();
+8 -4
View File
@@ -515,17 +515,21 @@ bool getUMDReplacePermit() {
static u32 sceUmdReplaceProhibit()
{
UMDReplacePermit = false;
DEBUG_LOG(SCEIO,"sceUmdReplaceProhibit()");
host->UpdateUI();
if (UMDReplacePermit) {
UMDReplacePermit = false;
host->NotifySwitchUMDUpdated();
}
return 0;
}
static u32 sceUmdReplacePermit()
{
UMDReplacePermit = true;
DEBUG_LOG(SCEIO,"sceUmdReplacePermit()");
host->UpdateUI();
if (!UMDReplacePermit) {
UMDReplacePermit = true;
host->NotifySwitchUMDUpdated();
}
return 0;
}
+11 -6
View File
@@ -137,6 +137,11 @@ static int sceUsbCamReadMicBlocking(u32 bufAddr, u32 size) {
return __MicInputBlocking(size >> 1, config->micParam.frequency, bufAddr);
}
static int sceUsbCamReadMic(u32 bufAddr, u32 size) {
INFO_LOG(HLE, "UNIMPL sceUsbCamReadMic: size: %d", size);
return __MicInputBlocking(size >> 1, config->micParam.frequency, bufAddr);
}
static int sceUsbCamSetupVideo(u32 paramAddr, u32 workareaAddr, int wasize) {
if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupVideoParam))) {
Memory::ReadStruct(paramAddr, &config->videoParam);
@@ -185,7 +190,7 @@ static int sceUsbCamReadVideoFrameBlocking(u32 bufAddr, u32 size) {
if (Memory::IsValidRange(bufAddr, size)) {
Memory::Memcpy(bufAddr, videoBuffer, transferSize);
}
return videoBufferLength;
return transferSize;
}
static int sceUsbCamReadVideoFrame(u32 bufAddr, u32 size) {
@@ -194,7 +199,7 @@ static int sceUsbCamReadVideoFrame(u32 bufAddr, u32 size) {
if (Memory::IsValidRange(bufAddr, size)) {
Memory::Memcpy(bufAddr, videoBuffer, transferSize);
}
nextVideoFrame = videoBufferLength;
nextVideoFrame = transferSize;
return 0;
}
@@ -243,7 +248,7 @@ const HLEFunction sceUsbCam[] =
{ 0X82A64030, &WrapI_V<sceUsbCamStartMic>, "sceUsbCamStartMic", 'i', "" },
{ 0X5145868A, &WrapI_V<sceUsbCamStopMic>, "sceUsbCamStopMic", 'i', "" },
{ 0X36636925, &WrapI_UU<sceUsbCamReadMicBlocking>, "sceUsbCamReadMicBlocking", 'i', "xx" },
{ 0X3DC0088E, nullptr, "sceUsbCamReadMic", '?', "" },
{ 0X3DC0088E, &WrapI_UU<sceUsbCamReadMic>, "sceUsbCamReadMic", 'i', "xx" },
{ 0XB048A67D, nullptr, "sceUsbCamWaitReadMicEnd", '?', "" },
{ 0XF8847F60, nullptr, "sceUsbCamPollReadMicEnd", '?', "" },
{ 0X5778B452, nullptr, "sceUsbCamGetMicDataLength", '?', "" },
@@ -301,7 +306,7 @@ void Register_sceUsbCam()
}
std::vector<std::string> Camera::getDeviceList() {
#ifdef HAVE_WIN32_CAMERA
#ifdef HAVE_WIN32_CAMERA
if (winCamera) {
return winCamera->getDeviceList();
}
@@ -321,7 +326,7 @@ int Camera::startCapture() {
INFO_LOG(HLE, "%s resolution: %dx%d", __FUNCTION__, width, height);
config->mode = Camera::Mode::Video;
#ifdef HAVE_WIN32_CAMERA
#ifdef HAVE_WIN32_CAMERA
if (winCamera) {
if (winCamera->isShutDown()) {
delete winCamera;
@@ -345,7 +350,7 @@ int Camera::startCapture() {
int Camera::stopCapture() {
INFO_LOG(HLE, "%s", __FUNCTION__);
#ifdef HAVE_WIN32_CAMERA
#ifdef HAVE_WIN32_CAMERA
if (winCamera) {
winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::STOP, nullptr });
}
+3 -1
View File
@@ -92,14 +92,16 @@ const HLEFunction sceUsbGps[] =
{0X268F95CA, nullptr, "sceUsbGpsSetInitDataLocation", '?', "" },
{0X31F95CDE, nullptr, "sceUsbGpsGetPowerSaveMode", '?', "" },
{0X54D26AA4, &WrapI_U<sceUsbGpsGetInitDataLocation>, "sceUsbGpsGetInitDataLocation", 'i', "x" },
{0X5881C826, nullptr, "sceUsbGpsGetStaticNavMode", '?', "" },
{0X63D1F89D, nullptr, "sceUsbGpsResetInitialPosition", '?', "" },
{0X69E4AAA8, nullptr, "sceUsbGpsSaveInitData", '?', "" },
{0X6EED4811, &WrapI_V<sceUsbGpsClose>, "sceUsbGpsClose", 'i', "" },
{0X7C16AC3A, &WrapI_U<sceUsbGpsGetState>, "sceUsbGpsGetState", 'i', "x"},
{0X7C16AC3A, &WrapI_U<sceUsbGpsGetState>, "sceUsbGpsGetState", 'i', "x" },
{0X934EC2B2, &WrapI_UU<sceUsbGpsGetData>, "sceUsbGpsGetData", 'i', "xx" },
{0X9D8F99E8, nullptr, "sceUsbGpsSetPowerSaveMode", '?', "" },
{0X9F267D34, &WrapI_V<sceUsbGpsOpen>, "sceUsbGpsOpen", 'i', "" },
{0XA259CD67, nullptr, "sceUsbGpsReset", '?', "" },
{0XA8ED0BC2, nullptr, "sceUsbGpsSetStaticNavMode", '?', "" },
};
void Register_sceUsbGps()
+1 -1
View File
@@ -43,7 +43,7 @@ typedef struct {
float garbage3;
float speed;
float bearing;
} GpsData;
} GpsData;
typedef struct {
unsigned char id;
+32 -8
View File
@@ -19,6 +19,7 @@
#include "Common/Serialize/Serializer.h"
#include "Common/Serialize/SerializeFuncs.h"
#include "Common/System/System.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/FunctionWrappers.h"
#include "Core/HLE/sceKernelThread.h"
@@ -74,6 +75,8 @@ static void __UsbMicAudioUpdate(u64 userdata, int cyclesLate) {
isNeedInput = false;
} else {
u64 waitTimeus = (waitingThread.needSize - Microphone::availableAudioBufSize()) * 1000000 / 2 / waitingThread.sampleRate;
if(eventUsbMicAudioUpdate == -1)
eventUsbMicAudioUpdate = CoreTiming::RegisterEvent("UsbMicAudioUpdate", &__UsbMicAudioUpdate);
CoreTiming::ScheduleEvent(usToCycles(waitTimeus), eventUsbMicAudioUpdate, userdata);
}
} else {
@@ -118,7 +121,7 @@ void __UsbMicShutdown() {
}
void __UsbMicDoState(PointerWrap &p) {
auto s = p.Section("sceUsbMic", 0, 1);
auto s = p.Section("sceUsbMic", 0, 2);
if (!s) {
return;
}
@@ -128,18 +131,25 @@ void __UsbMicDoState(PointerWrap &p) {
Do(p, curSampleRate);
Do(p, curChannels);
Do(p, micState);
// Maybe also need to save the state of audioBuf.
if (waitingThreads.size() != 0 && p.mode == PointerWrap::MODE_READ) {
u64 waitTimeus = (waitingThreads[0].needSize - Microphone::availableAudioBufSize()) * 1000000 / 2 / waitingThreads[0].sampleRate;
CoreTiming::ScheduleEvent(usToCycles(waitTimeus), eventUsbMicAudioUpdate, waitingThreads[0].threadID);
if (s > 1) {
Do(p, eventUsbMicAudioUpdate);
if (eventUsbMicAudioUpdate != -1) {
CoreTiming::RestoreRegisterEvent(eventUsbMicAudioUpdate, "UsbMicAudioUpdate", &__UsbMicAudioUpdate);
}
} else {
eventUsbMicAudioUpdate = -1;
}
if (!audioBuf && numNeedSamples > 0) {
audioBuf = new QueueBuf(numNeedSamples << 1);
}
if (micState == 0) {
if (Microphone::isMicStarted())
Microphone::stopMic();
} else if (micState == 1) {
if (Microphone::isMicStarted()) {
Microphone::stopMic();
Microphone::startMic(new std::vector<u32>({ curSampleRate, curChannels }));
// Ok, started.
} else {
Microphone::startMic(new std::vector<u32>({ curSampleRate, curChannels }));
}
@@ -302,6 +312,12 @@ int Microphone::startMic(void *param) {
#ifdef HAVE_WIN32_MICROPHONE
if (winMic)
winMic->sendMessage({ CAPTUREDEVIDE_COMMAND::START, param });
#elif PPSSPP_PLATFORM(ANDROID)
std::vector<u32> *micParam = static_cast<std::vector<u32>*>(param);
int sampleRate = micParam->at(0);
int channels = micParam->at(1);
INFO_LOG(HLE, "microphone_command : sr = %d", sampleRate);
System_SendMessage("microphone_command", ("startRecording:" + std::to_string(sampleRate)).c_str());
#endif
micState = 1;
return 0;
@@ -311,6 +327,8 @@ int Microphone::stopMic() {
#ifdef HAVE_WIN32_MICROPHONE
if (winMic)
winMic->sendMessage({ CAPTUREDEVIDE_COMMAND::STOP, nullptr });
#elif PPSSPP_PLATFORM(ANDROID)
System_SendMessage("microphone_command", "stopRecording");
#endif
micState = 0;
return 0;
@@ -319,6 +337,8 @@ int Microphone::stopMic() {
bool Microphone::isHaveDevice() {
#ifdef HAVE_WIN32_MICROPHONE
return winMic->getDeviceCounts() >= 1;
#elif PPSSPP_PLATFORM(ANDROID)
return audioRecording_Available();
#endif
return false;
}
@@ -327,6 +347,8 @@ bool Microphone::isMicStarted() {
#ifdef HAVE_WIN32_MICROPHONE
if(winMic)
return winMic->isStarted();
#elif PPSSPP_PLATFORM(ANDROID)
return audioRecording_State();
#endif
return false;
}
@@ -403,6 +425,8 @@ u32 __MicInputBlocking(u32 maxSamples, u32 sampleRate, u32 bufAddr) {
waitTimeus = (size - Microphone::availableAudioBufSize()) * 1000000 / 2 / sampleRate;
isNeedInput = true;
}
if(eventUsbMicAudioUpdate == -1)
eventUsbMicAudioUpdate = CoreTiming::RegisterEvent("UsbMicAudioUpdate", &__UsbMicAudioUpdate);
CoreTiming::ScheduleEvent(usToCycles(waitTimeus), eventUsbMicAudioUpdate, __KernelGetCurThread());
MicWaitInfo waitInfo = { __KernelGetCurThread(), bufAddr, size, sampleRate };
std::unique_lock<std::mutex> lock(wtMutex);
@@ -419,7 +443,7 @@ const HLEFunction sceUsbMic[] =
{0x06128E42, &WrapI_V<sceUsbMicPollInputEnd>, "sceUsbMicPollInputEnd", 'i', "" },
{0x2E6DCDCD, &WrapI_UUU<sceUsbMicInputBlocking>, "sceUsbMicInputBlocking", 'i', "xxx" },
{0x45310F07, &WrapI_U<sceUsbMicInputInitEx>, "sceUsbMicInputInitEx", 'i', "x" },
{0x5F7F368D, &WrapI_V<sceUsbMicInput> , "sceUsbMicInput", 'i', "" },
{0x5F7F368D, &WrapI_V<sceUsbMicInput>, "sceUsbMicInput", 'i', "" },
{0x63400E20, &WrapI_V<sceUsbMicGetInputLength>, "sceUsbMicGetInputLength", 'i', "" },
{0xB8E536EB, &WrapI_III<sceUsbMicInputInit>, "sceUsbMicInputInit", 'i', "iii" },
{0xF899001C, &WrapI_V<sceUsbMicWaitInputEnd>, "sceUsbMicWaitInputEnd", 'i', "" },
+48 -7
View File
@@ -145,6 +145,8 @@ MediaEngine::MediaEngine(): m_pdata(0) {
m_videoStream = -1;
m_audioStream = -1;
m_expectedVideoStreams = 0;
m_desWidth = 0;
m_desHeight = 0;
m_decodingsize = 0;
@@ -182,7 +184,7 @@ void MediaEngine::closeMedia() {
}
void MediaEngine::DoState(PointerWrap &p) {
auto s = p.Section("MediaEngine", 1, 5);
auto s = p.Section("MediaEngine", 1, 6);
if (!s)
return;
@@ -200,6 +202,11 @@ void MediaEngine::DoState(PointerWrap &p) {
} else {
m_mpegheaderReadPos = m_mpegheaderSize;
}
if (s >= 6) {
Do(p, m_expectedVideoStreams);
} else {
m_expectedVideoStreams = 0;
}
Do(p, m_ringbuffersize);
@@ -271,20 +278,22 @@ bool MediaEngine::SetupStreams() {
}
// Looking good. Let's add those streams.
const AVCodec *h264_codec = avcodec_find_decoder(AV_CODEC_ID_H264);
int videoStreamNum = -1;
for (int i = 0; i < numStreams; i++) {
const u8 *const currentStreamAddr = m_mpegheader + 0x82 + i * 16;
int streamId = currentStreamAddr[0];
// We only set video streams. We demux the audio stream separately.
if ((streamId & PSMF_VIDEO_STREAM_ID) == PSMF_VIDEO_STREAM_ID) {
AVStream *stream = avformat_new_stream(m_pFormatCtx, h264_codec);
stream->id = 0x00000100 | streamId;
stream->request_probe = 0;
stream->need_parsing = AVSTREAM_PARSE_FULL;
// We could set the width here, but we don't need to.
++videoStreamNum;
addVideoStream(videoStreamNum, streamId);
}
}
// Add the streams to meet the expectation.
for (int i = videoStreamNum + 1; i < m_expectedVideoStreams; i++) {
addVideoStream(i);
}
#endif
return true;
@@ -403,6 +412,38 @@ bool MediaEngine::reloadStream()
return loadStream(m_mpegheader, 2048, m_ringbuffersize);
}
bool MediaEngine::addVideoStream(int streamNum, int streamId) {
#ifdef USE_FFMPEG
if (m_pFormatCtx) {
// no need to add an existing stream.
if ((u32)streamNum < m_pFormatCtx->nb_streams)
return true;
const AVCodec *h264_codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!h264_codec)
return false;
AVStream *stream = avformat_new_stream(m_pFormatCtx, h264_codec);
if (stream) {
// Reference ISO/IEC 13818-1.
if (streamId == -1)
streamId = PSMF_VIDEO_STREAM_ID | streamNum;
stream->id = 0x00000100 | streamId;
stream->request_probe = 0;
stream->need_parsing = AVSTREAM_PARSE_FULL;
// We could set the width here, but we don't need to.
if (streamNum >= m_expectedVideoStreams) {
++m_expectedVideoStreams;
}
return true;
}
}
#endif
if (streamNum >= m_expectedVideoStreams) {
++m_expectedVideoStreams;
}
return false;
}
int MediaEngine::addStreamData(const u8 *buffer, int addSize) {
int size = addSize;
if (size > 0 && m_pdata) {
+2
View File
@@ -61,6 +61,7 @@ public:
void closeMedia();
bool loadStream(const u8 *buffer, int readSize, int RingbufferSize);
bool reloadStream();
bool addVideoStream(int streamNum, int streamId = -1);
// open the mpeg context
bool openContext(bool keepReadPos = false);
void closeContext();
@@ -116,6 +117,7 @@ public: // TODO: Very little of this below should be public.
int m_sws_fmt;
u8 *m_buffer;
int m_videoStream;
int m_expectedVideoStreams;
// Used by the demuxer.
int m_audioStream;
+28 -12
View File
@@ -15,6 +15,7 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <algorithm>
#include "Common/Serialize/Serializer.h"
#include "Common/Serialize/SerializeFuncs.h"
#include "Core/CoreTiming.h"
@@ -28,12 +29,13 @@ static MemStickState memStickState;
static MemStickFatState memStickFatState;
static bool memStickNeedsAssign = false;
static u64 memStickInsertedAt = 0;
static uint64_t memstickInitialFree = 0;
const u64 normalMemstickSize = 9ULL * 1024 * 1024 * 1024;
const u64 smallMemstickSize = 1ULL * 1024 * 1024 * 1024;
void MemoryStick_DoState(PointerWrap &p) {
auto s = p.Section("MemoryStick", 1, 4);
auto s = p.Section("MemoryStick", 1, 5);
if (!s)
return;
@@ -46,6 +48,9 @@ void MemoryStick_DoState(PointerWrap &p) {
u64 memStickSize = normalMemstickSize;
Do(p, memStickSize);
}
if (s >= 5) {
Do(p, memstickInitialFree);
}
if (s >= 3) {
Do(p, memStickNeedsAssign);
@@ -71,18 +76,28 @@ u64 MemoryStick_SectorSize() {
}
u64 MemoryStick_FreeSpace() {
// Cap the memory stick size to avoid math errors when old games get sizes that were
// hard to imagine back then.
// We have a compat setting to make it even smaller for Harry Potter : Goblet of Fire, see #13266.
const u64 memStickSize = PSP_CoreParameter().compat.flags().ReportSmallMemstick ? smallMemstickSize : (u64)g_Config.iMemStickSizeGB * 1024 * 1024 * 1024;
u64 usedSpace = pspFileSystem.getDirSize("ms0:/PSP/SAVEDATA/");// Assume the memory stick is only used to store savedata.
u64 freeSpace;
if (usedSpace >= memStickSize)
freeSpace = 0;
else
freeSpace = memStickSize - usedSpace;
const CompatFlags &flags = PSP_CoreParameter().compat.flags();
u64 realFreeSpace = pspFileSystem.FreeSpace("ms0:/");
return freeSpace;
// Cap the memory stick size to avoid math errors when old games get sizes that were
// not planned for back then (even though 2GB cards were available.)
// We have a compat setting to make it even smaller for Harry Potter : Goblet of Fire, see #13266.
const u64 memStickSize = flags.ReportSmallMemstick ? smallMemstickSize : (u64)g_Config.iMemStickSizeGB * 1024 * 1024 * 1024;
// Assume the memory stick is only used to store savedata.
u64 usedSpace = pspFileSystem.getDirSize("ms0:/PSP/SAVEDATA/");
u64 simulatedFreeSpace = 0;
if (usedSpace < memStickSize) {
simulatedFreeSpace = memStickSize - usedSpace;
}
if (flags.MemstickFixedFree) {
// Assassin's Creed: Bloodlines fails to save if free space changes incorrectly during game.
realFreeSpace = 0;
if (usedSpace <= memstickInitialFree) {
realFreeSpace = memstickInitialFree - usedSpace;
}
}
return std::min(simulatedFreeSpace, realFreeSpace);
}
void MemoryStick_SetFatState(MemStickFatState state) {
@@ -116,4 +131,5 @@ void MemoryStick_Init() {
}
memStickNeedsAssign = false;
memstickInitialFree = pspFileSystem.FreeSpace("ms0:/") + pspFileSystem.getDirSize("ms0:/PSP/SAVEDATA/");
}
+1 -9
View File
@@ -331,15 +331,7 @@ void ADSREnvelope::SetSimpleEnvelope(u32 ADSREnv1, u32 ADSREnv2) {
}
}
SasInstance::SasInstance()
: maxVoices(PSP_SAS_VOICES_MAX),
sampleRate(44100),
outputMode(PSP_SAS_OUTPUTMODE_MIXED),
mixBuffer(0),
sendBuffer(0),
sendBufferDownsampled(0),
sendBufferProcessed(0),
grainSize(0) {
SasInstance::SasInstance() {
#ifdef AUDIO_TO_FILE
audioDump = fopen("D:\\audio.raw", "wb");
#endif
+9 -9
View File
@@ -282,16 +282,16 @@ public:
int GetGrainSize() const { return grainSize; }
int EstimateMixUs();
int maxVoices;
int sampleRate;
int outputMode;
int maxVoices = PSP_SAS_VOICES_MAX;
int sampleRate = 44100;
int outputMode = PSP_SAS_OUTPUTMODE_MIXED;
int *mixBuffer;
int *sendBuffer;
s16 *sendBufferDownsampled;
s16 *sendBufferProcessed;
int *mixBuffer = nullptr;
int *sendBuffer = nullptr;
s16 *sendBufferDownsampled = nullptr;
s16 *sendBufferProcessed = nullptr;
FILE *audioDump;
FILE *audioDump = nullptr;
void Mix(u32 outAddr, u32 inAddr = 0, int leftVol = 0, int rightVol = 0);
void MixVoice(SasVoice &voice);
@@ -310,6 +310,6 @@ public:
private:
SasReverb reverb_;
int grainSize;
int grainSize = 0;
int16_t mixTemp_[PSP_SAS_MAX_GRAIN * 4 + 2 + 8]; // some extra margin for very high pitches.
};
+1 -1
View File
@@ -396,7 +396,7 @@ u32 AuCtx::AuSetLoopNum(int loop)
// return 1 to read more data stream, 0 don't read
int AuCtx::AuCheckStreamDataNeeded() {
// If we would ask for bytes, then some are needed.
if (AuStreamBytesNeeded() != 0) {
if (AuStreamBytesNeeded() > 0) {
return 1;
}
return 0;
+2
View File
@@ -57,6 +57,8 @@ public:
virtual void NotifyUserMessage(const std::string &message, float duration = 1.0f, u32 color = 0x00FFFFFF, const char *id = nullptr) {}
virtual void SendUIMessage(const std::string &message, const std::string &value) {}
virtual void NotifySwitchUMDUpdated() {}
// Used for headless.
virtual bool ShouldSkipUI() { return false; }
virtual void SendDebugOutput(const std::string &output) {}
+8 -26
View File
@@ -386,37 +386,19 @@ namespace MIPSComp
}
void ArmJit::Comp_Cache(MIPSOpcode op) {
// int imm = (s16)(op & 0xFFFF);
// int rs = _RS;
// int addr = R(rs) + imm;
CONDITIONAL_DISABLE(LSU);
int func = (op >> 16) & 0x1F;
// It appears that a cache line is 0x40 (64) bytes, loops in games
// issue the cache instruction at that interval.
// These codes might be PSP-specific, they don't match regular MIPS cache codes very well
// See Int_Cache for the definitions.
switch (func) {
// Icache
case 8:
// Invalidate the instruction cache at this address
DISABLE;
break;
// Dcache
case 24:
// "Create Dirty Exclusive" - for avoiding a cacheline fill before writing to it.
// Will cause garbage on the real machine so we just ignore it, the app will overwrite the cacheline.
break;
case 25: // Hit Invalidate - zaps the line if present in cache. Should not writeback???? scary.
// No need to do anything.
break;
case 27: // D-cube. Hit Writeback Invalidate. Tony Hawk Underground 2
break;
case 30: // GTA LCS, a lot. Fill (prefetch). Tony Hawk Underground 2
break;
case 24: break;
case 25: break;
case 27: break;
case 30: break;
default:
// Fall back to the interpreter.
DISABLE;
break;
}
}
}
+1
View File
@@ -16,6 +16,7 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "ppsspp_config.h"
#if PPSSPP_ARCH(ARM64)
#include "Common/Log.h"
+11 -26
View File
@@ -311,6 +311,7 @@ namespace MIPSComp {
case 40: //sb
case 41: //sh
case 43: //sw
#ifndef MASKED_PSP_MEMORY
if (jo.cachePointers && g_Config.bFastMemory) {
// ARM has smaller load/store immediate displacements than MIPS, 12 bits - and some memory ops only have 8 bits.
int offsetRange = 0x3ff;
@@ -327,6 +328,7 @@ namespace MIPSComp {
gpr.MapReg(rt, load ? MAP_NOINIT : 0);
targetReg = gpr.R(rt);
}
switch (o) {
case 35: LDR(INDEX_UNSIGNED, targetReg, gpr.RPtr(rs), offset); break;
case 37: LDRH(INDEX_UNSIGNED, targetReg, gpr.RPtr(rs), offset); break;
@@ -342,6 +344,7 @@ namespace MIPSComp {
break;
}
}
#endif
if (!load && gpr.IsImm(rt) && gpr.TryMapTempImm(rt) != INVALID_REG) {
// We're storing an immediate value, let's see if we can optimize rt.
@@ -432,37 +435,19 @@ namespace MIPSComp {
}
void Arm64Jit::Comp_Cache(MIPSOpcode op) {
// int imm = (s16)(op & 0xFFFF);
// int rs = _RS;
// int addr = R(rs) + imm;
CONDITIONAL_DISABLE(LSU);
int func = (op >> 16) & 0x1F;
// It appears that a cache line is 0x40 (64) bytes, loops in games
// issue the cache instruction at that interval.
// These codes might be PSP-specific, they don't match regular MIPS cache codes very well
// See Int_Cache for the definitions.
switch (func) {
// Icache
case 8:
// Invalidate the instruction cache at this address
DISABLE;
break;
// Dcache
case 24:
// "Create Dirty Exclusive" - for avoiding a cacheline fill before writing to it.
// Will cause garbage on the real machine so we just ignore it, the app will overwrite the cacheline.
break;
case 25: // Hit Invalidate - zaps the line if present in cache. Should not writeback???? scary.
// No need to do anything.
break;
case 27: // D-cube. Hit Writeback Invalidate. Tony Hawk Underground 2
break;
case 30: // GTA LCS, a lot. Fill (prefetch). Tony Hawk Underground 2
break;
case 24: break;
case 25: break;
case 27: break;
case 30: break;
default:
// Fall back to the interpreter.
DISABLE;
break;
}
}
}
-1
View File
@@ -358,7 +358,6 @@ namespace MIPSComp {
ADD(SCRATCH1_64, SCRATCH1_64, MEMBASEREG);
}
}
fp.STP(32, INDEX_SIGNED, fpr.V(vregs[0]), fpr.V(vregs[1]), SCRATCH1_64, 0);
fp.STP(32, INDEX_SIGNED, fpr.V(vregs[2]), fpr.V(vregs[3]), SCRATCH1_64, 8);
+4
View File
@@ -100,6 +100,10 @@ Arm64Jit::Arm64Jit(MIPSState *mips) : blocks(mips, this), gpr(mips, &js, &jo), f
jo.enablePointerify = false;
}
#ifdef MASKED_PSP_MEMORY
jo.enablePointerify = false;
#endif
logBlocks = 0;
dontLogBlocks = 0;
blocks.Init();
+6 -26
View File
@@ -108,37 +108,17 @@ namespace MIPSComp {
void IRFrontend::Comp_Cache(MIPSOpcode op) {
CONDITIONAL_DISABLE(LSU);
// int imm = (s16)(op & 0xFFFF);
// int rs = _RS;
// int addr = R(rs) + imm;
int func = (op >> 16) & 0x1F;
// It appears that a cache line is 0x40 (64) bytes, loops in games
// issue the cache instruction at that interval.
// These codes might be PSP-specific, they don't match regular MIPS cache codes very well
// See Int_Cache for the definitions.
switch (func) {
// Icache
case 8:
// Invalidate the instruction cache at this address
DISABLE;
break;
// Dcache
case 24:
// "Create Dirty Exclusive" - for avoiding a cacheline fill before writing to it.
// Will cause garbage on the real machine so we just ignore it, the app will overwrite the cacheline.
break;
case 25: // Hit Invalidate - zaps the line if present in cache. Should not writeback???? scary.
// No need to do anything.
break;
case 27: // D-cube. Hit Writeback Invalidate. Tony Hawk Underground 2
break;
case 30: // GTA LCS, a lot. Fill (prefetch). Tony Hawk Underground 2
break;
case 24: break;
case 25: break;
case 27: break;
case 30: break;
default:
// Fall back to the interpreter.
DISABLE;
break;
}
}
}
+2
View File
@@ -102,6 +102,8 @@ namespace MIPSInt
// issue the cache instruction at that interval.
// These codes might be PSP-specific, they don't match regular MIPS cache codes very well
// NOTE: If you add support for more, make sure they are handled in the various Jit::Comp_Cache.
switch (func) {
// Icache
case 8:
+14 -1
View File
@@ -401,7 +401,20 @@ namespace MIPSComp {
}
void Jit::Comp_Cache(MIPSOpcode op) {
DISABLE;
CONDITIONAL_DISABLE(LSU);
int func = (op >> 16) & 0x1F;
// See Int_Cache for the definitions.
switch (func) {
case 24: break;
case 25: break;
case 27: break;
case 30: break;
default:
// Fall back to the interpreter.
DISABLE;
}
}
}
+1 -1
View File
@@ -121,7 +121,7 @@ static MemoryView views[] =
static const int num_views = sizeof(views) / sizeof(MemoryView);
inline static bool CanIgnoreView(const MemoryView &view) {
#if PPSSPP_ARCH(32BIT)
#ifdef MASKED_PSP_MEMORY
// Basically, 32-bit platforms can ignore views that are masked out anyway.
return (view.flags & MV_MIRROR_PREVIOUS) && (view.virtual_address & ~MEMVIEW32_MASK) != 0;
#else
+8 -1
View File
@@ -84,7 +84,7 @@ extern u32 g_PSPModel;
// UWP has such limited memory management that we need to mask
// even in 64-bit mode. Also, when using the sanitizer, we need to mask as well.
#if PPSSPP_ARCH(32BIT) || PPSSPP_PLATFORM(UWP) || USE_ADDRESS_SANITIZER
#if PPSSPP_ARCH(32BIT) || PPSSPP_PLATFORM(UWP) || USE_ADDRESS_SANITIZER || PPSSPP_PLATFORM(IOS)
#define MASKED_PSP_MEMORY
#endif
@@ -261,10 +261,17 @@ inline bool IsVRAMAddress(const u32 address) {
inline bool IsDepthTexVRAMAddress(const u32 address) {
return ((address & 0x3FE00000) == 0x04200000) || ((address & 0x3FE00000) == 0x04600000);
}
// 0x08000000 -> 0x08800000
inline bool IsKernelAddress(const u32 address) {
return ((address & 0x3F800000) == 0x08000000);
}
// 0x08000000 -> 0x08400000
inline bool IsKernelAndNotVolatileAddress(const u32 address) {
return ((address & 0x3FC00000) == 0x08000000);
}
bool IsScratchpadAddress(const u32 address);
// Used for auto-converted char * parameters, which can sometimes legitimately be null -
+1 -1
View File
@@ -17,7 +17,7 @@
#pragma once
#include "CommonTypes.h"
#include "Common/CommonTypes.h"
// Broken out of MemMap.h to avoid a bad include dependency.
-16
View File
@@ -18,12 +18,8 @@
#include "ppsspp_config.h"
#include <algorithm>
#ifdef USING_QT_UI
#include <QtGui/QImage>
#else
#include <png.h>
#include "ext/jpge/jpge.h"
#endif
#include "Common/ColorConv.h"
#include "Common/File/FileUtil.h"
@@ -35,7 +31,6 @@
#include "GPU/GPUInterface.h"
#include "GPU/GPUState.h"
#ifndef USING_QT_UI
// This is used to make non-ASCII paths work for filename.
// Technically only needed on Windows.
class JPEGFileStream : public jpge::output_stream
@@ -118,7 +113,6 @@ static bool WriteScreenshotToPNG(png_imagep image, const char *filename, int con
return false;
}
}
#endif
static bool ConvertPixelTo8888RGBA(GPUDebugBufferFormat fmt, u8 &r, u8 &g, u8 &b, u8 &a, const void *buffer, int offset, bool rev) {
const u8 *buf8 = (const u8 *)buffer;
@@ -348,10 +342,6 @@ bool TakeGameScreenshot(const char *filename, ScreenshotFormat fmt, ScreenshotTy
}
bool Save888RGBScreenshot(const char *filename, ScreenshotFormat fmt, const u8 *bufferRGB888, int w, int h) {
#ifdef USING_QT_UI
QImage image(bufferRGB888, w, h, QImage::Format_RGB888);
return image.save(filename, fmt == ScreenshotFormat::PNG ? "PNG" : "JPG");
#else
if (fmt == ScreenshotFormat::PNG) {
png_image png;
memset(&png, 0, sizeof(png));
@@ -374,14 +364,9 @@ bool Save888RGBScreenshot(const char *filename, ScreenshotFormat fmt, const u8 *
} else {
return false;
}
#endif
}
bool Save8888RGBAScreenshot(const char *filename, const u8 *buffer, int w, int h) {
#ifdef USING_QT_UI
QImage image(buffer, w, h, QImage::Format_RGBA8888);
return image.save(filename, "PNG");
#else
png_image png;
memset(&png, 0, sizeof(png));
png.version = PNG_IMAGE_VERSION;
@@ -396,5 +381,4 @@ bool Save8888RGBAScreenshot(const char *filename, const u8 *buffer, int w, int h
success = false;
}
return success;
#endif
}
+1
View File
@@ -26,6 +26,7 @@
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <string>
#include <thread>
+12 -8
View File
@@ -38,6 +38,12 @@ void GenerateDepalShader300(char *buffer, GEBufferFormat pixelFormat, ShaderLang
WRITE(p, "SamplerState texSamp : register(s0);\n");
WRITE(p, "Texture2D<float4> tex : register(t0);\n");
WRITE(p, "Texture2D<float4> pal : register(t3);\n");
// Support for depth.
if (pixelFormat == GE_FORMAT_DEPTH16) {
WRITE(p, "cbuffer params : register(b0) {\n");
WRITE(p, " float z_scale; float z_offset;\n");
WRITE(p, "};\n");
}
} else if (language == GLSL_VULKAN) {
WRITE(p, "#version 450\n");
WRITE(p, "#extension GL_ARB_separate_shader_objects : enable\n");
@@ -53,7 +59,6 @@ void GenerateDepalShader300(char *buffer, GEBufferFormat pixelFormat, ShaderLang
WRITE(p, " float z_scale; float z_offset;\n");
WRITE(p, "};\n");
}
} else {
if (gl_extensions.IsGLES) {
WRITE(p, "#version 300 es\n");
@@ -159,7 +164,7 @@ void GenerateDepalShader300(char *buffer, GEBufferFormat pixelFormat, ShaderLang
void GenerateDepalShaderFloat(char *buffer, GEBufferFormat pixelFormat, ShaderLanguage lang) {
char *p = buffer;
const char *modFunc = lang == HLSL_DX9 ? "fmod" : "mod";
const char *modFunc = lang == HLSL_D3D9 ? "fmod" : "mod";
char lookupMethod[128] = "index.r";
char offset[128] = "";
@@ -281,7 +286,7 @@ void GenerateDepalShaderFloat(char *buffer, GEBufferFormat pixelFormat, ShaderLa
float texel_offset = ((float)clutBase + 0.5f) / texturePixels;
sprintf(offset, " + %f", texel_offset);
if (lang == GLSL_140) {
if (lang == GLSL_1xx) {
if (gl_extensions.IsGLES) {
WRITE(p, "#version 100\n");
WRITE(p, "precision mediump float;\n");
@@ -300,7 +305,7 @@ void GenerateDepalShaderFloat(char *buffer, GEBufferFormat pixelFormat, ShaderLa
WRITE(p, " float coord = (%s * %f)%s;\n", lookupMethod, index_multiplier, offset);
WRITE(p, " gl_FragColor = texture2D(pal, vec2(coord, 0.0));\n");
WRITE(p, "}\n");
} else if (lang == HLSL_DX9) {
} else if (lang == HLSL_D3D9) {
WRITE(p, "sampler tex: register(s0);\n");
WRITE(p, "sampler pal: register(s1);\n");
WRITE(p, "float4 main(float2 v_texcoord0 : TEXCOORD0) : COLOR0 {\n");
@@ -313,18 +318,17 @@ void GenerateDepalShaderFloat(char *buffer, GEBufferFormat pixelFormat, ShaderLa
void GenerateDepalShader(char *buffer, GEBufferFormat pixelFormat, ShaderLanguage language) {
switch (language) {
case GLSL_140:
case GLSL_1xx:
GenerateDepalShaderFloat(buffer, pixelFormat, language);
break;
case GLSL_300:
case GLSL_3xx:
case GLSL_VULKAN:
case HLSL_D3D11:
GenerateDepalShader300(buffer, pixelFormat, language);
break;
case HLSL_DX9:
case HLSL_D3D9:
GenerateDepalShaderFloat(buffer, pixelFormat, language);
break;
case HLSL_D3D11_LEVEL9:
default:
_assert_msg_(false, "Depal shader language not supported: %d", (int)language);
}
+3 -2
View File
@@ -18,8 +18,9 @@
#pragma once
#include <cstdint>
#include "Common/GPU/Shader.h"
#include "GPU/ge_constants.h"
#include "GPU/Common/ShaderCommon.h"
static const int DEPAL_TEXTURE_OLD_AGE = 120;
@@ -32,4 +33,4 @@ public:
protected:
uint32_t GenerateShaderID(uint32_t clutMode, GEBufferFormat pixelFormat) const;
uint32_t GetClutID(GEPaletteFormat clutFormat, uint32_t clutHash) const;
};
};
+3 -2
View File
@@ -480,8 +480,9 @@ u32 DrawEngineCommon::NormalizeVertices(u8 *outPtr, u8 *bufPtr, const u8 *inPtr,
return GE_VTYPE_TC_FLOAT | GE_VTYPE_COL_8888 | GE_VTYPE_NRM_FLOAT | GE_VTYPE_POS_FLOAT | (vertType & (GE_VTYPE_IDX_MASK | GE_VTYPE_THROUGH));
}
bool DrawEngineCommon::ApplyShaderBlending() {
bool DrawEngineCommon::ApplyFramebufferRead(bool *fboTexNeedsBind) {
if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) {
*fboTexNeedsBind = false;
return true;
}
@@ -502,7 +503,7 @@ bool DrawEngineCommon::ApplyShaderBlending() {
return false;
}
fboTexNeedBind_ = true;
*fboTexNeedsBind = true;
gstate_c.Dirty(DIRTY_SHADERBLEND);
return true;
+2 -2
View File
@@ -112,7 +112,7 @@ protected:
// Vertex decoding
void DecodeVertsStep(u8 *dest, int &i, int &decodedVerts);
bool ApplyShaderBlending();
bool ApplyFramebufferRead(bool *fboTexNeedsBind);
inline int IndexSize(u32 vtype) const {
const u32 indexType = (vtype & GE_VTYPE_IDX_MASK);
@@ -169,7 +169,7 @@ protected:
GEPrimitiveType prevPrim_ = GE_PRIM_INVALID;
// Shader blending state
bool fboTexNeedBind_ = false;
bool fboTexNeedsBind_ = false;
bool fboTexBound_ = false;
// Hardware tessellation
File diff suppressed because it is too large. Load diff
@@ -17,12 +17,11 @@
#pragma once
#include "GPU/Common/ShaderId.h"
#include "GPU/Common/ShaderCommon.h"
#include "Common/GPU/Shader.h"
namespace DX9 {
struct FShaderID;
bool GenerateFragmentShaderHLSL(const FShaderID &id, char *buffer, ShaderLanguage lang = HLSL_DX9);
// D3D9 constants
#define CONST_PS_TEXENV 0
#define CONST_PS_ALPHACOLORREF 1
@@ -38,4 +37,4 @@ bool GenerateFragmentShaderHLSL(const FShaderID &id, char *buffer, ShaderLanguag
// For stencil upload
#define CONST_PS_STENCILVALUE 10
};
bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLanguageDesc &compat, uint64_t *uniformMask, std::string *errorString);
+307 -145
View File
@@ -35,7 +35,9 @@
#include "GPU/Common/PostShader.h"
#include "GPU/Common/PresentationCommon.h"
#include "GPU/Common/TextureCacheCommon.h"
#include "GPU/Common/ReinterpretFramebuffer.h"
#include "GPU/Debugger/Record.h"
#include "GPU/Debugger/Stepping.h"
#include "GPU/GPUInterface.h"
#include "GPU/GPUState.h"
@@ -77,6 +79,7 @@ bool FramebufferManagerCommon::UpdateSize() {
renderWidth_ = (float)PSP_CoreParameter().renderWidth;
renderHeight_ = (float)PSP_CoreParameter().renderHeight;
renderScaleFactor_ = (float)PSP_CoreParameter().renderScaleFactor;
pixelWidth_ = PSP_CoreParameter().pixelWidth;
pixelHeight_ = PSP_CoreParameter().pixelHeight;
bloomHack_ = g_Config.iBloomHack;
@@ -267,10 +270,13 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
if (v->fb_address == params.fb_address) {
vfb = v;
// Update fb stride in case it changed
if (vfb->fb_stride != params.fb_stride || vfb->format != params.fmt) {
vfbFormatChanged = true;
if (vfb->fb_stride != params.fb_stride) {
vfb->fb_stride = params.fb_stride;
vfbFormatChanged = true;
}
if (vfb->format != params.fmt) {
vfb->format = params.fmt;
vfbFormatChanged = true;
}
if (vfb->z_address == 0 && vfb->z_stride == 0 && params.z_stride != 0) {
@@ -339,14 +345,6 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
}
}
float renderWidthFactor = renderWidth_ / 480.0f;
float renderHeightFactor = renderHeight_ / 272.0f;
if (PSP_CoreParameter().compat.flags().Force04154000Download && params.fb_address == 0x04154000) {
renderWidthFactor = 1.0;
renderHeightFactor = 1.0;
}
// None found? Create one.
if (!vfb) {
vfb = new VirtualFramebuffer{};
@@ -355,28 +353,28 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
vfb->fb_stride = params.fb_stride;
vfb->z_address = params.z_address;
vfb->z_stride = params.z_stride;
// The other width/height parameters are set in ResizeFramebufFBO below.
vfb->width = drawing_width;
vfb->height = drawing_height;
vfb->newWidth = drawing_width;
vfb->newHeight = drawing_height;
vfb->lastFrameNewSize = gpuStats.numFlips;
vfb->renderWidth = (u16)(drawing_width * renderWidthFactor);
vfb->renderHeight = (u16)(drawing_height * renderHeightFactor);
vfb->bufferWidth = drawing_width;
vfb->bufferHeight = drawing_height;
vfb->format = params.fmt;
vfb->drawnFormat = params.fmt;
vfb->usageFlags = FB_USAGE_RENDERTARGET;
SetColorUpdated(vfb, skipDrawReason);
u32 byteSize = ColorBufferByteSize(vfb);
if (Memory::IsVRAMAddress(params.fb_address) && params.fb_address + byteSize > framebufRangeEnd_) {
framebufRangeEnd_ = params.fb_address + byteSize;
}
// This is where we actually create the framebuffer. The true is "force".
ResizeFramebufFBO(vfb, drawing_width, drawing_height, true);
NotifyRenderFramebufferCreated(vfb);
SetColorUpdated(vfb, skipDrawReason);
INFO_LOG(FRAMEBUF, "Creating FBO for %08x (z: %08x) : %i x %i x %i", vfb->fb_address, vfb->z_address, vfb->width, vfb->height, vfb->format);
vfb->last_frame_render = gpuStats.numFlips;
@@ -473,7 +471,7 @@ void FramebufferManagerCommon::BlitFramebufferDepth(VirtualFramebuffer *src, Vir
int h = std::min(src->renderHeight, dst->renderHeight);
// Note: We prefer Blit ahead of Copy here, since at least on GL, Copy will always also copy stencil which we don't want. See #9740.
if (gstate_c.Supports(GPU_SUPPORTS_FRAMEBUFFER_BLIT)) {
if (gstate_c.Supports(GPU_SUPPORTS_FRAMEBUFFER_BLIT_TO_DEPTH)) {
draw_->BlitFramebuffer(src->fbo, 0, 0, w, h, dst->fbo, 0, 0, w, h, Draw::FB_DEPTH_BIT, Draw::FB_BLIT_NEAREST, "BlitFramebufferDepth");
RebindFramebuffer("BlitFramebufferDepth");
} else if (gstate_c.Supports(GPU_SUPPORTS_COPY_IMAGE)) {
@@ -507,7 +505,7 @@ void FramebufferManagerCommon::NotifyRenderFramebufferUpdated(VirtualFramebuffer
if (vfbFormatChanged) {
textureCache_->NotifyFramebuffer(vfb, NOTIFY_FB_UPDATED);
if (vfb->drawnFormat != vfb->format) {
ReformatFramebufferFrom(vfb, vfb->drawnFormat);
ReinterpretFramebufferFrom(vfb, vfb->drawnFormat);
}
}
@@ -521,6 +519,120 @@ void FramebufferManagerCommon::NotifyRenderFramebufferUpdated(VirtualFramebuffer
}
}
void FramebufferManagerCommon::ReinterpretFramebufferFrom(VirtualFramebuffer *vfb, GEBufferFormat oldFormat) {
if (!useBufferedRendering_ || !vfb->fbo) {
return;
}
ShaderLanguage lang = draw_->GetShaderLanguageDesc().shaderLanguage;
bool doReinterpret = PSP_CoreParameter().compat.flags().ReinterpretFramebuffers &&
(lang == HLSL_D3D11 || lang == GLSL_VULKAN || lang == GLSL_3xx);
if (!doReinterpret) {
// Fake reinterpret - just clear the way we always did on Vulkan. Just clear color and stencil.
if (oldFormat == GE_FORMAT_565) {
// We have to bind here instead of clear, since it can be that no framebuffer is bound.
// The backend can sometimes directly optimize it to a clear.
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::KEEP, Draw::RPAction::CLEAR }, "FakeReinterpret");
// Need to dirty anything that has command buffer dynamic state, in case we started a new pass above.
// Should find a way to feed that information back, maybe... Or simply correct the issue in the rendermanager.
gstate_c.Dirty(DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE);
}
return;
}
GEBufferFormat newFormat = vfb->format;
_assert_(newFormat != oldFormat);
// We only reinterpret between 16 - bit formats, for now.
if (!IsGeBufferFormat16BitColor(oldFormat) || !IsGeBufferFormat16BitColor(newFormat)) {
// 16->32 and 32->16 will require some more specialized shaders.
return;
}
char *vsCode = nullptr;
char *fsCode = nullptr;
if (!reinterpretVS_) {
vsCode = new char[4000];
const ShaderLanguageDesc &shaderLanguageDesc = draw_->GetShaderLanguageDesc();
GenerateReinterpretVertexShader(vsCode, shaderLanguageDesc);
reinterpretVS_ = draw_->CreateShaderModule(ShaderStage::Vertex, shaderLanguageDesc.shaderLanguage, (const uint8_t *)vsCode, strlen(vsCode), "reinterpret_vs");
_assert_(reinterpretVS_);
}
if (!reinterpretSampler_) {
Draw::SamplerStateDesc samplerDesc{};
samplerDesc.magFilter = Draw::TextureFilter::LINEAR;
samplerDesc.minFilter = Draw::TextureFilter::LINEAR;
reinterpretSampler_ = draw_->CreateSamplerState(samplerDesc);
}
// See if we need to create a new pipeline.
Draw::Pipeline *pipeline = reinterpretFromTo_[(int)oldFormat][(int)newFormat];
if (!pipeline) {
fsCode = new char[4000];
const ShaderLanguageDesc &shaderLanguageDesc = draw_->GetShaderLanguageDesc();
GenerateReinterpretFragmentShader(fsCode, oldFormat, newFormat, shaderLanguageDesc);
Draw::ShaderModule *reinterpretFS = draw_->CreateShaderModule(ShaderStage::Fragment, shaderLanguageDesc.shaderLanguage, (const uint8_t *)fsCode, strlen(fsCode), "reinterpret_fs");
_assert_(reinterpretFS);
std::vector<Draw::ShaderModule *> shaders;
shaders.push_back(reinterpretVS_);
shaders.push_back(reinterpretFS);
using namespace Draw;
Draw::PipelineDesc desc{};
// We use a "fullscreen triangle".
// TODO: clear the stencil buffer. Hard to actually initialize it with the new alpha, though possible - let's see if
// we need it.
DepthStencilState *depth = draw_->CreateDepthStencilState({ false, false, Comparison::LESS });
BlendState *blendstateOff = draw_->CreateBlendState({ false, 0xF });
RasterState *rasterNoCull = draw_->CreateRasterState({});
// No uniforms for these, only a single texture input.
PipelineDesc pipelineDesc{ Primitive::TRIANGLE_LIST, shaders, nullptr, depth, blendstateOff, rasterNoCull, nullptr };
pipeline = draw_->CreateGraphicsPipeline(pipelineDesc);
_assert_(pipeline != nullptr);
reinterpretFromTo_[(int)oldFormat][(int)newFormat] = pipeline;
depth->Release();
blendstateOff->Release();
rasterNoCull->Release();
reinterpretFS->Release();
}
// Copy to a temp framebuffer.
Draw::Framebuffer *temp = GetTempFBO(TempFBO::REINTERPRET, vfb->renderWidth, vfb->renderHeight);
draw_->InvalidateCachedState();
draw_->CopyFramebufferImage(vfb->fbo, 0, 0, 0, 0, temp, 0, 0, 0, 0, vfb->renderWidth, vfb->renderHeight, 1, Draw::FBChannel::FB_COLOR_BIT, "reinterpret_prep");
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "reinterpret");
draw_->BindPipeline(pipeline);
draw_->BindFramebufferAsTexture(temp, 0, Draw::FBChannel::FB_COLOR_BIT, 0);
draw_->BindSamplerStates(0, 1, &reinterpretSampler_);
draw_->SetScissorRect(0, 0, vfb->renderWidth, vfb->renderHeight);
Draw::Viewport vp = Draw::Viewport{ 0.0f, 0.0f, (float)vfb->renderWidth, (float)vfb->renderHeight, 0.0f, 1.0f };
draw_->SetViewports(1, &vp);
// No vertex buffer - generate vertices in shader. TODO: Switch to a vertex buffer for GLES2/D3D9 compat.
draw_->Draw(3, 0);
draw_->InvalidateCachedState();
// Unbind.
draw_->BindTexture(0, nullptr);
RebindFramebuffer("After reinterpret");
shaderManager_->DirtyLastShader();
textureCache_->ForgetLastTexture();
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VERTEXSHADER_STATE);
delete[] vsCode;
delete[] fsCode;
}
void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffer *prevVfb, VirtualFramebuffer *vfb, bool isClearingDepth) {
if (ShouldDownloadFramebuffer(vfb) && !vfb->memoryUpdated) {
ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height);
@@ -549,8 +661,7 @@ void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffe
}
}
if (vfb->drawnFormat != vfb->format) {
// TODO: Might ultimately combine this with the resize step in DoSetRenderFrameBuffer().
ReformatFramebufferFrom(vfb, vfb->drawnFormat);
ReinterpretFramebufferFrom(vfb, vfb->drawnFormat);
}
if (useBufferedRendering_) {
@@ -708,6 +819,49 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int
}
}
bool FramebufferManagerCommon::BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags) {
if (!framebuffer->fbo || !useBufferedRendering_) {
draw_->BindTexture(0, nullptr);
gstate_c.skipDrawReason |= SKIPDRAW_BAD_FB_TEXTURE;
return false;
}
// currentRenderVfb_ will always be set when this is called, except from the GE debugger.
// Let's just not bother with the copy in that case.
bool skipCopy = (flags & BINDFBCOLOR_MAY_COPY) == 0;
if (GPUStepping::IsStepping()) {
skipCopy = true;
}
// Currently rendering to this framebuffer. Need to make a copy.
if (!skipCopy && framebuffer == currentRenderVfb_) {
// TODO: Maybe merge with bvfbs_? Not sure if those could be packing, and they're created at a different size.
Draw::Framebuffer *renderCopy = GetTempFBO(TempFBO::COPY, framebuffer->renderWidth, framebuffer->renderHeight);
if (renderCopy) {
VirtualFramebuffer copyInfo = *framebuffer;
copyInfo.fbo = renderCopy;
CopyFramebufferForColorTexture(&copyInfo, framebuffer, flags);
RebindFramebuffer("After BindFramebufferAsColorTexture");
draw_->BindFramebufferAsTexture(renderCopy, stage, Draw::FB_COLOR_BIT, 0);
} else {
draw_->BindFramebufferAsTexture(framebuffer->fbo, stage, Draw::FB_COLOR_BIT, 0);
}
return true;
} else if (framebuffer != currentRenderVfb_ || (flags & BINDFBCOLOR_FORCE_SELF) != 0) {
draw_->BindFramebufferAsTexture(framebuffer->fbo, stage, Draw::FB_COLOR_BIT, 0);
return true;
} else {
ERROR_LOG_REPORT_ONCE(vulkanSelfTexture, G3D, "Attempting to texture from target (src=%08x / target=%08x / flags=%d)", framebuffer->fb_address, currentRenderVfb_->fb_address, flags);
// To do this safely in Vulkan, we need to use input attachments.
// Actually if the texture region and render regions don't overlap, this is safe, but we need
// to transition to GENERAL image layout which will take some trickery.
// Badness on D3D11 to bind the currently rendered-to framebuffer as a texture.
draw_->BindTexture(0, nullptr);
gstate_c.skipDrawReason |= SKIPDRAW_BAD_FB_TEXTURE;
return false;
}
}
void FramebufferManagerCommon::CopyFramebufferForColorTexture(VirtualFramebuffer *dst, VirtualFramebuffer *src, int flags) {
int x = 0;
int y = 0;
@@ -1045,6 +1199,7 @@ void FramebufferManagerCommon::DecimateFBOs() {
}
}
// Requires width/height to be set already.
void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, int h, bool force, bool skipCopy) {
_dbg_assert_(w > 0);
_dbg_assert_(h > 0);
@@ -1066,12 +1221,36 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w,
vfb->bufferHeight = std::max((int)vfb->bufferHeight, h);
}
SetRenderSize(vfb);
bool force1x = false;
switch (bloomHack_) {
case 1:
force1x = vfb->bufferWidth <= 128 || vfb->bufferHeight <= 64;
break;
case 2:
force1x = vfb->bufferWidth <= 256 || vfb->bufferHeight <= 128;
break;
case 3:
force1x = vfb->bufferWidth < 480 || vfb->bufferWidth > 800 || vfb->bufferHeight < 272; // GOW uses 864x272
break;
}
if (PSP_CoreParameter().compat.flags().Force04154000Download && vfb->fb_address == 0x04154000) {
force1x = true;
}
if (force1x && g_Config.iInternalResolution != 1) {
vfb->renderScaleFactor = 1.0f;
vfb->renderWidth = vfb->bufferWidth;
vfb->renderHeight = vfb->bufferHeight;
} else {
vfb->renderScaleFactor = renderScaleFactor_;
vfb->renderWidth = (u16)(vfb->bufferWidth * renderScaleFactor_);
vfb->renderHeight = (u16)(vfb->bufferHeight * renderScaleFactor_);
}
// During hardware rendering, we always render at full color depth even if the game wouldn't on real hardware.
// It's not worth the trouble trying to support lower bit-depth rendering, just
// more cases to test that nobody will ever use.
vfb->colorDepth = Draw::FBO_8888;
textureCache_->ForgetLastTexture();
@@ -1089,8 +1268,8 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w,
shaderManager_->DirtyLastShader();
char tag[256];
snprintf(tag, sizeof(tag), "%08x_%08x_%dx%d", vfb->fb_address, vfb->z_address, w, h);
vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, 1, true, (Draw::FBColorDepth)vfb->colorDepth, tag });
snprintf(tag, sizeof(tag), "%08x_%08x_%dx%d_%s", vfb->fb_address, vfb->z_address, w, h, GeBufferFormatToString(vfb->format));
vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, 1, true, tag });
if (old.fbo) {
INFO_LOG(FRAMEBUF, "Resizing FBO for %08x : %dx%dx%s", vfb->fb_address, w, h, GeBufferFormatToString(vfb->format));
if (vfb->fbo) {
@@ -1182,9 +1361,13 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size,
}
}
if (!dstBuffer && srcBuffer && PSP_CoreParameter().compat.flags().BlockTransferAllowCreateFB) {
dstBuffer = CreateRAMFramebuffer(dst, srcBuffer->width, srcBuffer->height, srcBuffer->fb_stride, srcBuffer->format);
dstY = 0;
if (!dstBuffer && srcBuffer) {
// Note - if we're here, we're in a memcpy, not a block transfer. Not allowing IntraVRAMBlockTransferAllowCreateFB.
// Technically, that makes BlockTransferAllowCreateFB a bit of a misnomer.
if (PSP_CoreParameter().compat.flags().BlockTransferAllowCreateFB) {
dstBuffer = CreateRAMFramebuffer(dst, srcBuffer->width, srcBuffer->height, srcBuffer->fb_stride, srcBuffer->format);
dstY = 0;
}
}
if (dstBuffer) {
dstBuffer->last_frame_used = gpuStats.numFlips;
@@ -1310,21 +1493,25 @@ void FramebufferManagerCommon::FindTransferFramebuffers(VirtualFramebuffer *&dst
}
}
if (srcBuffer && !dstBuffer && PSP_CoreParameter().compat.flags().BlockTransferAllowCreateFB) {
GEBufferFormat ramFormat;
// Try to guess the appropriate format. We only know the bpp from the block transfer command (16 or 32 bit).
if (bpp == 4) {
// Only one possibility unless it's doing split pixel tricks (which we could detect through stride maybe).
ramFormat = GE_FORMAT_8888;
} else if (srcBuffer->format != GE_FORMAT_8888) {
// We guess that the game will interpret the data the same as it was in the source of the copy.
// Seems like a likely good guess, and works in Test Drive Unlimited.
ramFormat = srcBuffer->format;
} else {
// No info left - just fall back to something. But this is definitely split pixel tricks.
ramFormat = GE_FORMAT_5551;
if (srcBuffer && !dstBuffer) {
if (PSP_CoreParameter().compat.flags().BlockTransferAllowCreateFB ||
(PSP_CoreParameter().compat.flags().IntraVRAMBlockTransferAllowCreateFB &&
Memory::IsVRAMAddress(srcBuffer->fb_address) && Memory::IsVRAMAddress(dstBasePtr))) {
GEBufferFormat ramFormat;
// Try to guess the appropriate format. We only know the bpp from the block transfer command (16 or 32 bit).
if (bpp == 4) {
// Only one possibility unless it's doing split pixel tricks (which we could detect through stride maybe).
ramFormat = GE_FORMAT_8888;
} else if (srcBuffer->format != GE_FORMAT_8888) {
// We guess that the game will interpret the data the same as it was in the source of the copy.
// Seems like a likely good guess, and works in Test Drive Unlimited.
ramFormat = srcBuffer->format;
} else {
// No info left - just fall back to something. But this is definitely split pixel tricks.
ramFormat = GE_FORMAT_5551;
}
dstBuffer = CreateRAMFramebuffer(dstBasePtr, dstWidth, dstHeight, dstStride, ramFormat);
}
dstBuffer = CreateRAMFramebuffer(dstBasePtr, dstWidth, dstHeight, dstStride, ramFormat);
}
if (dstBuffer)
@@ -1341,9 +1528,6 @@ void FramebufferManagerCommon::FindTransferFramebuffers(VirtualFramebuffer *&dst
}
VirtualFramebuffer *FramebufferManagerCommon::CreateRAMFramebuffer(uint32_t fbAddress, int width, int height, int stride, GEBufferFormat format) {
float renderWidthFactor = renderWidth_ / 480.0f;
float renderHeightFactor = renderHeight_ / 272.0f;
INFO_LOG(G3D, "Creating RAM framebuffer at %08x (%dx%d, stride %d, format %d)", fbAddress, width, height, stride, format);
// A target for the destination is missing - so just create one!
@@ -1360,8 +1544,9 @@ VirtualFramebuffer *FramebufferManagerCommon::CreateRAMFramebuffer(uint32_t fbAd
vfb->newWidth = vfb->width;
vfb->newHeight = vfb->height;
vfb->lastFrameNewSize = gpuStats.numFlips;
vfb->renderWidth = (u16)(vfb->width * renderWidthFactor);
vfb->renderHeight = (u16)(vfb->height * renderHeightFactor);
vfb->renderScaleFactor = renderScaleFactor_;
vfb->renderWidth = (u16)(vfb->width * renderScaleFactor_);
vfb->renderHeight = (u16)(vfb->height * renderScaleFactor_);
vfb->bufferWidth = vfb->width;
vfb->bufferHeight = vfb->height;
vfb->format = format;
@@ -1371,7 +1556,7 @@ VirtualFramebuffer *FramebufferManagerCommon::CreateRAMFramebuffer(uint32_t fbAd
char name[64];
snprintf(name, sizeof(name), "%08x_color_RAM", vfb->fb_address);
textureCache_->NotifyFramebuffer(vfb, NOTIFY_FB_CREATED);
vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, 1, true, (Draw::FBColorDepth)vfb->colorDepth, name });
vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, 1, true, name });
vfbs_.push_back(vfb);
u32 byteSize = ColorBufferByteSize(vfb);
@@ -1414,16 +1599,19 @@ VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFram
nvfb->height = vfb->height;
nvfb->renderWidth = vfb->bufferWidth;
nvfb->renderHeight = vfb->bufferHeight;
nvfb->renderScaleFactor = 1.0f; // For readbacks we resize to the original size, of course.
nvfb->bufferWidth = vfb->bufferWidth;
nvfb->bufferHeight = vfb->bufferHeight;
nvfb->format = vfb->format;
nvfb->drawnWidth = vfb->drawnWidth;
nvfb->drawnHeight = vfb->drawnHeight;
nvfb->drawnFormat = vfb->format;
nvfb->colorDepth = vfb->colorDepth;
if (!CreateDownloadTempBuffer(nvfb)) {
delete nvfb;
char name[64];
snprintf(name, sizeof(name), "download_temp");
nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, false, name });
if (!nvfb->fbo) {
ERROR_LOG(FRAMEBUF, "Error creating FBO! %d x %d", nvfb->renderWidth, nvfb->renderHeight);
return nullptr;
}
@@ -1439,36 +1627,6 @@ VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFram
return nvfb;
}
bool FramebufferManagerCommon::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) {
// When updating VRAM, it need to be exact format.
if (!gstate_c.Supports(GPU_PREFER_CPU_DOWNLOAD)) {
switch (nvfb->format) {
case GE_FORMAT_4444:
nvfb->colorDepth = Draw::FBO_4444;
break;
case GE_FORMAT_5551:
nvfb->colorDepth = Draw::FBO_5551;
break;
case GE_FORMAT_565:
nvfb->colorDepth = Draw::FBO_565;
break;
case GE_FORMAT_8888:
default:
nvfb->colorDepth = Draw::FBO_8888;
break;
}
}
char name[64];
snprintf(name, sizeof(name), "download_temp");
nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, false, (Draw::FBColorDepth)nvfb->colorDepth, name });
if (!nvfb->fbo) {
ERROR_LOG(FRAMEBUF, "Error creating FBO! %d x %d", nvfb->renderWidth, nvfb->renderHeight);
return false;
}
return true;
}
void FramebufferManagerCommon::ApplyClearToMemory(int x1, int y1, int x2, int y2, u32 clearColor) {
if (currentRenderVfb_) {
if ((currentRenderVfb_->usageFlags & FB_USAGE_DOWNLOAD_CLEAR) != 0) {
@@ -1547,36 +1705,6 @@ void FramebufferManagerCommon::ApplyClearToMemory(int x1, int y1, int x2, int y2
}
}
void FramebufferManagerCommon::OptimizeDownloadRange(VirtualFramebuffer * vfb, int & x, int & y, int & w, int & h) {
if (gameUsesSequentialCopies_) {
// Ignore the x/y/etc., read the entire thing.
x = 0;
y = 0;
w = vfb->width;
h = vfb->height;
}
if (x == 0 && y == 0 && w == vfb->width && h == vfb->height) {
// Mark it as fully downloaded until next render to it.
vfb->memoryUpdated = true;
vfb->usageFlags |= FB_USAGE_DOWNLOAD;
} else {
// Let's try to set the flag eventually, if the game copies a lot.
// Some games copy subranges very frequently.
const static int FREQUENT_SEQUENTIAL_COPIES = 3;
static int frameLastCopy = 0;
static u32 bufferLastCopy = 0;
static int copiesThisFrame = 0;
if (frameLastCopy != gpuStats.numFlips || bufferLastCopy != vfb->fb_address) {
frameLastCopy = gpuStats.numFlips;
bufferLastCopy = vfb->fb_address;
copiesThisFrame = 0;
}
if (++copiesThisFrame > FREQUENT_SEQUENTIAL_COPIES) {
gameUsesSequentialCopies_ = true;
}
}
}
bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int width, int height, int bpp, u32 skipDrawReason) {
if (!useBufferedRendering_) {
return false;
@@ -1705,35 +1833,6 @@ void FramebufferManagerCommon::NotifyBlockTransferAfter(u32 dstBasePtr, int dstS
}
}
void FramebufferManagerCommon::SetRenderSize(VirtualFramebuffer *vfb) {
float renderWidthFactor = renderWidth_ / 480.0f;
float renderHeightFactor = renderHeight_ / 272.0f;
bool force1x = false;
switch (bloomHack_) {
case 1:
force1x = vfb->bufferWidth <= 128 || vfb->bufferHeight <= 64;
break;
case 2:
force1x = vfb->bufferWidth <= 256 || vfb->bufferHeight <= 128;
break;
case 3:
force1x = vfb->bufferWidth < 480 || vfb->bufferWidth > 800 || vfb->bufferHeight < 272; // GOW uses 864x272
break;
}
if (PSP_CoreParameter().compat.flags().Force04154000Download && vfb->fb_address == 0x04154000) {
force1x = true;
}
if (force1x && g_Config.iInternalResolution != 1) {
vfb->renderWidth = vfb->bufferWidth;
vfb->renderHeight = vfb->bufferHeight;
} else {
vfb->renderWidth = (u16)(vfb->bufferWidth * renderWidthFactor);
vfb->renderHeight = (u16)(vfb->bufferHeight * renderHeightFactor);
}
}
void FramebufferManagerCommon::SetSafeSize(u16 w, u16 h) {
VirtualFramebuffer *vfb = currentRenderVfb_;
if (vfb) {
@@ -1745,10 +1844,11 @@ void FramebufferManagerCommon::SetSafeSize(u16 w, u16 h) {
void FramebufferManagerCommon::Resized() {
gstate_c.skipDrawReason &= ~SKIPDRAW_NON_DISPLAYED_FB;
int w, h;
presentation_->CalculateRenderResolution(&w, &h, &postShaderIsUpscalingFilter_, &postShaderIsSupersampling_);
int w, h, scaleFactor;
presentation_->CalculateRenderResolution(&w, &h, &scaleFactor, &postShaderIsUpscalingFilter_, &postShaderIsSupersampling_);
PSP_CoreParameter().renderWidth = w;
PSP_CoreParameter().renderHeight = h;
PSP_CoreParameter().renderScaleFactor = scaleFactor;
if (UpdateSize()) {
DestroyAllFBOs();
@@ -1786,10 +1886,15 @@ void FramebufferManagerCommon::DestroyAllFBOs() {
tempFB.second.fbo->Release();
}
tempFBOs_.clear();
for (auto iter : fbosToDelete_) {
iter->Release();
}
fbosToDelete_.clear();
}
Draw::Framebuffer *FramebufferManagerCommon::GetTempFBO(TempFBO reason, u16 w, u16 h, Draw::FBColorDepth color_depth) {
u64 key = ((u64)reason << 48) | ((u64)color_depth << 32) | ((u32)w << 16) | h;
Draw::Framebuffer *FramebufferManagerCommon::GetTempFBO(TempFBO reason, u16 w, u16 h) {
u64 key = ((u64)reason << 48) | ((u32)w << 16) | h;
auto it = tempFBOs_.find(key);
if (it != tempFBOs_.end()) {
it->second.last_frame_used = gpuStats.numFlips;
@@ -1799,7 +1904,7 @@ Draw::Framebuffer *FramebufferManagerCommon::GetTempFBO(TempFBO reason, u16 w, u
bool z_stencil = reason == TempFBO::STENCIL;
char name[128];
snprintf(name, sizeof(name), "temp_fbo_%dx%d%s", w, h, z_stencil ? "_depth" : "");
Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ w, h, 1, 1, z_stencil, color_depth, name });
Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ w, h, 1, 1, z_stencil, name });
if (!fbo) {
return nullptr;
}
@@ -1880,6 +1985,7 @@ bool FramebufferManagerCommon::GetFramebuffer(u32 fb_address, int fb_stride, GEB
tempVfb.bufferHeight = vfb->height;
tempVfb.renderWidth = w;
tempVfb.renderHeight = h;
tempVfb.renderScaleFactor = (float)maxRes;
BlitFramebuffer(&tempVfb, 0, 0, vfb, 0, 0, vfb->width, vfb->height, 0);
bound = tempFBO;
@@ -2036,7 +2142,35 @@ void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb,
}
if (vfb && vfb->fbo) {
// We'll pseudo-blit framebuffers here to get a resized version of vfb.
OptimizeDownloadRange(vfb, x, y, w, h);
if (gameUsesSequentialCopies_) {
// Ignore the x/y/etc., read the entire thing.
x = 0;
y = 0;
w = vfb->width;
h = vfb->height;
vfb->memoryUpdated = true;
vfb->usageFlags |= FB_USAGE_DOWNLOAD;
} else if (x == 0 && y == 0 && w == vfb->width && h == vfb->height) {
// Mark it as fully downloaded until next render to it.
vfb->memoryUpdated = true;
vfb->usageFlags |= FB_USAGE_DOWNLOAD;
} else {
// Let's try to set the flag eventually, if the game copies a lot.
// Some games copy subranges very frequently.
const static int FREQUENT_SEQUENTIAL_COPIES = 3;
static int frameLastCopy = 0;
static u32 bufferLastCopy = 0;
static int copiesThisFrame = 0;
if (frameLastCopy != gpuStats.numFlips || bufferLastCopy != vfb->fb_address) {
frameLastCopy = gpuStats.numFlips;
bufferLastCopy = vfb->fb_address;
copiesThisFrame = 0;
}
if (++copiesThisFrame > FREQUENT_SEQUENTIAL_COPIES) {
gameUsesSequentialCopies_ = true;
}
}
if (vfb->renderWidth == vfb->width && vfb->renderHeight == vfb->height) {
// No need to blit
PackFramebufferSync_(vfb, x, y, w, h);
@@ -2080,7 +2214,8 @@ void FramebufferManagerCommon::DownloadFramebufferForClut(u32 fb_address, u32 lo
// No need to download if we already have it.
if (w > 0 && h > 0 && !vfb->memoryUpdated && vfb->clutUpdatedBytes < loadBytes) {
// We intentionally don't call OptimizeDownloadRange() here - we don't want to over download.
// We intentionally don't try to optimize into a full download here - we don't want to over download.
// CLUT framebuffers are often incorrectly estimated in size.
if (x == 0 && y == 0 && w == vfb->width && h == vfb->height) {
vfb->memoryUpdated = true;
@@ -2128,3 +2263,30 @@ std::vector<FramebufferInfo> FramebufferManagerCommon::GetFramebufferList() {
return list;
}
void FramebufferManagerCommon::DeviceLost() {
DestroyAllFBOs();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (reinterpretFromTo_[i][j]) {
reinterpretFromTo_[i][j]->Release();
reinterpretFromTo_[i][j] = nullptr;
}
}
}
if (reinterpretSampler_) {
reinterpretSampler_->Release();
reinterpretSampler_ = nullptr;
}
if (reinterpretVS_) {
reinterpretVS_->Release();
reinterpretVS_ = nullptr;
}
presentation_->DeviceLost();
draw_ = nullptr;
}
void FramebufferManagerCommon::DeviceRestore(Draw::DrawContext *draw) {
draw_ = draw;
presentation_->DeviceRestore(draw);
}
+27 -14
View File
@@ -55,31 +55,32 @@ struct VirtualFramebuffer {
int fb_stride;
int z_stride;
GEBufferFormat format; // virtual, right now they are all RGBA8888
GEBufferFormat format; // virtual, in reality they are all RGBA8888 for better quality but we can reinterpret that as necessary
// width/height: The detected size of the current framebuffer, in original PSP pixels.
u16 width;
u16 height;
// bufferWidth/bufferHeight: The pre-scaling size of the buffer itself. May only be bigger than or equal to width/height.
// Actual physical buffer is this size times the render resolution multiplier.
// The buffer may be used to render a width or height from 0 to these values without being recreated.
u16 bufferWidth;
u16 bufferHeight;
// renderWidth/renderHeight: The scaled size we render at. May be scaled to render at higher resolutions.
// The physical buffer may be larger than renderWidth/renderHeight.
u16 renderWidth;
u16 renderHeight;
// bufferWidth/bufferHeight: The pre-scaling size of the buffer itself. May only be bigger than width/height.
// Actual physical buffer is this size times the render resolution multiplier.
// The buffer may be used to render a width or height from 0 to these values without being recreated.
u16 bufferWidth;
u16 bufferHeight;
float renderScaleFactor;
u16 usageFlags;
u16 newWidth;
u16 newHeight;
int lastFrameNewSize;
// TODO: Handle fbo and colorDepth better.
u8 colorDepth;
Draw::Framebuffer *fbo;
u16 drawnWidth;
@@ -157,6 +158,8 @@ enum class TempFBO {
BLIT,
// For copies of framebuffers (e.g. shader blending.)
COPY,
// For another type of framebuffers that can happen together with COPY (see Outrun)
REINTERPRET,
// Used to copy stencil data, means we need a stencil backing.
STENCIL,
};
@@ -188,7 +191,7 @@ class TextureCacheCommon;
class FramebufferManagerCommon {
public:
explicit FramebufferManagerCommon(Draw::DrawContext *draw);
FramebufferManagerCommon(Draw::DrawContext *draw);
virtual ~FramebufferManagerCommon();
virtual void Init();
@@ -232,6 +235,7 @@ public:
bool NotifyBlockTransferBefore(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason);
void NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason);
bool BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags);
void ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h);
void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes);
@@ -304,7 +308,10 @@ public:
virtual void Resized();
virtual void DestroyAllFBOs();
Draw::Framebuffer *GetTempFBO(TempFBO reason, u16 w, u16 h, Draw::FBColorDepth colorDepth = Draw::FBO_8888);
virtual void DeviceLost();
virtual void DeviceRestore(Draw::DrawContext *draw);
Draw::Framebuffer *GetTempFBO(TempFBO reason, u16 w, u16 h);
// Debug features
virtual bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes);
@@ -315,6 +322,7 @@ public:
const std::vector<VirtualFramebuffer *> &Framebuffers() {
return vfbs_;
}
void ReinterpretFramebufferFrom(VirtualFramebuffer *vfb, GEBufferFormat old);
protected:
virtual void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h);
@@ -339,7 +347,6 @@ protected:
void NotifyRenderFramebufferUpdated(VirtualFramebuffer *vfb, bool vfbFormatChanged);
void NotifyRenderFramebufferSwitched(VirtualFramebuffer *prevVfb, VirtualFramebuffer *vfb, bool isClearingDepth);
virtual void ReformatFramebufferFrom(VirtualFramebuffer *vfb, GEBufferFormat old) = 0;
void BlitFramebufferDepth(VirtualFramebuffer *src, VirtualFramebuffer *dst);
void ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, int h, bool force = false, bool skipCopy = false);
@@ -349,11 +356,9 @@ protected:
void DownloadFramebufferOnSwitch(VirtualFramebuffer *vfb);
void FindTransferFramebuffers(VirtualFramebuffer *&dstBuffer, VirtualFramebuffer *&srcBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int &srcWidth, int &srcHeight, int &dstWidth, int &dstHeight, int bpp);
VirtualFramebuffer *FindDownloadTempBuffer(VirtualFramebuffer *vfb);
virtual bool CreateDownloadTempBuffer(VirtualFramebuffer *nvfb);
virtual void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) = 0;
VirtualFramebuffer *CreateRAMFramebuffer(uint32_t fbAddress, int width, int height, int stride, GEBufferFormat format);
void OptimizeDownloadRange(VirtualFramebuffer *vfb, int &x, int &y, int &w, int &h);
void UpdateFramebufUsage(VirtualFramebuffer *vfb);
@@ -400,9 +405,10 @@ protected:
bool gameUsesSequentialCopies_ = false;
// Sampled in BeginFrame for safety.
// Sampled in BeginFrame/UpdateSize for safety.
float renderWidth_ = 0.0f;
float renderHeight_ = 0.0f;
float renderScaleFactor_ = 1.0f;
int pixelWidth_;
int pixelHeight_;
int bloomHack_ = 0;
@@ -424,4 +430,11 @@ protected:
FBO_OLD_AGE = 5,
FBO_OLD_USAGE_FLAG = 15,
};
// Thin3D stuff for reinterpreting image data between the various 16-bit formats.
// Safe, not optimal - there might be input attachment tricks, etc, but we can't use them
// since we don't want N different implementations.
Draw::Pipeline *reinterpretFromTo_[3][3]{};
Draw::ShaderModule *reinterpretVS_ = nullptr;
Draw::SamplerState *reinterpretSampler_ = nullptr;
};
+93 -30
View File
@@ -253,7 +253,7 @@ StencilValueType ReplaceAlphaWithStencilType() {
return STENCIL_VALUE_KEEP;
}
ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bufferFormat) {
ReplaceBlendType ReplaceBlendWithShader(bool allowFramebufferRead, GEBufferFormat bufferFormat) {
if (!gstate.isAlphaBlendEnabled() || gstate.isModeClear()) {
return REPLACE_BLEND_NO;
}
@@ -262,14 +262,14 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu
// Let's get the non-factor modes out of the way first.
switch (eq) {
case GE_BLENDMODE_ABSDIFF:
return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
case GE_BLENDMODE_MIN:
case GE_BLENDMODE_MAX:
if (gstate_c.Supports(GPU_SUPPORTS_BLEND_MINMAX)) {
return REPLACE_BLEND_STANDARD;
} else {
return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
}
default:
@@ -292,19 +292,19 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu
return REPLACE_BLEND_2X_ALPHA;
// Can't double, we need the source color to be correct.
// Doubling only alpha would clamp the src alpha incorrectly.
return !allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
case GE_DSTBLEND_DOUBLEDSTALPHA:
case GE_DSTBLEND_DOUBLEINVDSTALPHA:
if (bufferFormat == GE_FORMAT_565)
return REPLACE_BLEND_2X_ALPHA;
return !allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
case GE_DSTBLEND_DOUBLESRCALPHA:
// We can't technically do this correctly (due to clamping) without reading the dst color.
// Using a copy isn't accurate either, though, when there's overlap.
if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH))
return !allowShaderBlend ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
return !allowFramebufferRead ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
return REPLACE_BLEND_PRE_SRC_2X_ALPHA;
case GE_DSTBLEND_DOUBLEINVSRCALPHA:
@@ -331,7 +331,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu
return REPLACE_BLEND_STANDARD;
}
// Can't double, we need the source color to be correct.
return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
case GE_DSTBLEND_DOUBLEDSTALPHA:
case GE_DSTBLEND_DOUBLEINVDSTALPHA:
@@ -340,7 +340,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu
// Doubling will have no effect here.
return REPLACE_BLEND_STANDARD;
}
return !allowShaderBlend ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO;
return !allowFramebufferRead ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO;
case GE_DSTBLEND_DOUBLESRCALPHA:
case GE_DSTBLEND_DOUBLEINVSRCALPHA:
@@ -349,7 +349,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu
}
// Double both src (for dst alpha) and alpha (for dst factor.)
// But to be accurate (clamping), we need to read the dst color.
return !allowShaderBlend ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
return !allowFramebufferRead ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
case GE_DSTBLEND_SRCALPHA:
case GE_DSTBLEND_INVSRCALPHA:
@@ -361,7 +361,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu
return REPLACE_BLEND_STANDARD;
}
// We can't technically do this correctly (due to clamping) without reading the dst alpha.
return !allowShaderBlend ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO;
return !allowFramebufferRead ? REPLACE_BLEND_2X_SRC : REPLACE_BLEND_COPY_FBO;
}
case GE_SRCBLEND_DOUBLEINVDSTALPHA:
@@ -375,14 +375,14 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu
if (bufferFormat == GE_FORMAT_565) {
return REPLACE_BLEND_STANDARD;
}
return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
case GE_DSTBLEND_DOUBLESRCALPHA:
case GE_DSTBLEND_DOUBLEINVSRCALPHA:
if (bufferFormat == GE_FORMAT_565) {
return REPLACE_BLEND_2X_ALPHA;
}
return !allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
case GE_DSTBLEND_SRCALPHA:
case GE_DSTBLEND_INVSRCALPHA:
@@ -393,7 +393,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu
if (bufferFormat == GE_FORMAT_565) {
return REPLACE_BLEND_STANDARD;
}
return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
}
case GE_SRCBLEND_FIXA:
@@ -401,7 +401,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu
switch (funcB) {
case GE_DSTBLEND_DOUBLESRCALPHA:
// Can't safely double alpha, will clamp.
return !allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
case GE_DSTBLEND_DOUBLEINVSRCALPHA:
// Doubling alpha is safe for the inverse, will clamp to zero either way.
@@ -412,7 +412,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu
if (bufferFormat == GE_FORMAT_565) {
return REPLACE_BLEND_STANDARD;
}
return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
case GE_DSTBLEND_FIXB:
default:
@@ -446,14 +446,14 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu
if (funcA == GE_SRCBLEND_SRCALPHA || funcA == GE_SRCBLEND_INVSRCALPHA) {
// Can't safely double alpha, will clamp. However, a copy may easily be worse due to overlap.
if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH))
return !allowShaderBlend ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
return !allowFramebufferRead ? REPLACE_BLEND_PRE_SRC_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
return REPLACE_BLEND_PRE_SRC_2X_ALPHA;
} else {
// This means dst alpha/color is used in the src factor.
// Unfortunately, copying here causes overlap problems in Silent Hill games (it seems?)
// We will just hope that doubling alpha for the dst factor will not clamp too badly.
if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH))
return !allowShaderBlend ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
return !allowFramebufferRead ? REPLACE_BLEND_2X_ALPHA : REPLACE_BLEND_COPY_FBO;
return REPLACE_BLEND_2X_ALPHA;
}
@@ -470,7 +470,7 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu
if (bufferFormat == GE_FORMAT_565) {
return REPLACE_BLEND_STANDARD;
}
return !allowShaderBlend ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
return !allowFramebufferRead ? REPLACE_BLEND_STANDARD : REPLACE_BLEND_COPY_FBO;
default:
return REPLACE_BLEND_STANDARD;
@@ -906,7 +906,7 @@ static void ApplyLogicOp(BlendFactor &srcBlend, BlendFactor &dstBlend, BlendEq &
}
// Try to simulate some common logic ops.
void ApplyStencilReplaceAndLogicOp(ReplaceAlphaType replaceAlphaWithStencil, GenericBlendState &blendState) {
void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithStencil, GenericBlendState &blendState) {
StencilValueType stencilType = STENCIL_VALUE_KEEP;
if (replaceAlphaWithStencil == REPLACE_ALPHA_YES) {
stencilType = ReplaceAlphaWithStencilType();
@@ -956,9 +956,72 @@ void ApplyStencilReplaceAndLogicOp(ReplaceAlphaType replaceAlphaWithStencil, Gen
}
}
// Called even if AlphaBlendEnable == false - it also deals with stencil-related blend state.
bool IsColorWriteMaskComplex(bool allowFramebufferRead) {
// Restrict to Outrun temporarily (by uglily reusing the ReinterpretFramebuffers flag)
// This check must match the one in ConvertMaskState.
if (!allowFramebufferRead || !PSP_CoreParameter().compat.flags().ReinterpretFramebuffers) {
// Don't have a choice - we'll make do but it won't always be right.
return false;
}
void ConvertBlendState(GenericBlendState &blendState, bool allowShaderBlend) {
uint32_t colorMask = (gstate.pmskc & 0xFFFFFF) | (gstate.pmska << 24);
for (int i = 0; i < 4; i++) {
switch (colorMask & 0xFF) {
case 0x0:
case 0xFF:
break;
default:
return true;
}
colorMask >>= 8;
}
return false;
}
// If we can we emulate the colorMask by simply toggling the full R G B A masks offered
// by modern hardware, we do that. This is 99.9% of the time.
// When that's not enough, we fall back on a technique similar to shader blending,
// we read from the framebuffer (or a copy of it).
void ConvertMaskState(GenericMaskState &maskState, bool allowFramebufferRead) {
// Invert to convert masks from the PSP's format where 1 is don't draw to PC where 1 is draw.
uint32_t colorMask = ~((gstate.pmskc & 0xFFFFFF) | (gstate.pmska << 24));
maskState.applyFramebufferRead = false;
for (int i = 0; i < 4; i++) {
int channelMask = colorMask & 0xFF;
switch (channelMask) {
case 0x0:
maskState.rgba[i] = false;
break;
case 0xFF:
maskState.rgba[i] = true;
break;
default:
if (allowFramebufferRead) {
// Instead of just 'true', restrict shader bitmasks to Outrun temporarily (by uglily reusing the ReinterpretFramebuffers flag)
// TODO: This check must match the one in IsColorWriteMaskComplex.
maskState.applyFramebufferRead = PSP_CoreParameter().compat.flags().ReinterpretFramebuffers;
maskState.rgba[i] = true;
} else {
// Use the old heuristic.
maskState.rgba[i] = channelMask >= 128;
}
}
colorMask >>= 8;
}
// Let's not write to alpha if stencil isn't enabled.
if (IsStencilTestOutputDisabled()) {
maskState.rgba[3] = false;
} else if (ReplaceAlphaWithStencilType() == STENCIL_VALUE_KEEP) {
// If the stencil type is set to KEEP, we shouldn't write to the stencil/alpha channel.
maskState.rgba[3] = false;
}
}
// Called even if AlphaBlendEnable == false - it also deals with stencil-related blend state.
void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead) {
// Blending is a bit complex to emulate. This is due to several reasons:
//
// * Doubled blend modes (src, dst, inversed) aren't supported in OpenGL.
@@ -969,25 +1032,25 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowShaderBlend) {
//
// If we can't apply blending, we make a copy of the framebuffer and do it manually.
blendState.applyShaderBlending = false;
blendState.dirtyShaderBlend = false;
blendState.applyFramebufferRead = false;
blendState.dirtyShaderBlendFixValues = false;
blendState.useBlendColor = false;
blendState.replaceAlphaWithStencil = REPLACE_ALPHA_NO;
ReplaceBlendType replaceBlend = ReplaceBlendWithShader(allowShaderBlend, gstate.FrameBufFormat());
ReplaceBlendType replaceBlend = ReplaceBlendWithShader(allowFramebufferRead, gstate.FrameBufFormat());
ReplaceAlphaType replaceAlphaWithStencil = ReplaceAlphaWithStencil(replaceBlend);
bool usePreSrc = false;
switch (replaceBlend) {
case REPLACE_BLEND_NO:
blendState.resetShaderBlending = true;
blendState.resetFramebufferRead = true;
// We may still want to do something about stencil -> alpha.
ApplyStencilReplaceAndLogicOp(replaceAlphaWithStencil, blendState);
ApplyStencilReplaceAndLogicOpIgnoreBlend(replaceAlphaWithStencil, blendState);
return;
case REPLACE_BLEND_COPY_FBO:
blendState.applyShaderBlending = true;
blendState.resetShaderBlending = false;
blendState.applyFramebufferRead = true;
blendState.resetFramebufferRead = false;
blendState.replaceAlphaWithStencil = replaceAlphaWithStencil;
break;
@@ -1003,7 +1066,7 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowShaderBlend) {
}
blendState.enabled = true;
blendState.resetShaderBlending = true;
blendState.resetFramebufferRead = true;
const GEBlendMode blendFuncEq = gstate.getBlendEq();
GEBlendSrcFactor blendFuncA = gstate.getBlendFuncA();
@@ -1071,7 +1134,7 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowShaderBlend) {
glBlendFuncA = BlendFactor::ONE;
// Need to pull in the fixed color. TODO: If it hasn't changed, no need to dirty.
if (blendFuncA == GE_SRCBLEND_FIXA) {
blendState.dirtyShaderBlend = true;
blendState.dirtyShaderBlendFixValues = true;
}
}
+24 -5
View File
@@ -1,5 +1,6 @@
#pragma once
#include <cstdint>
#include "Common/CommonTypes.h"
#include "GPU/ge_constants.h"
@@ -25,12 +26,18 @@ enum ReplaceAlphaType {
};
enum ReplaceBlendType {
REPLACE_BLEND_NO,
REPLACE_BLEND_NO, // Blend function handled directly with blend states.
REPLACE_BLEND_STANDARD,
// SRC part of blend function handled in-shader.
REPLACE_BLEND_PRE_SRC,
REPLACE_BLEND_PRE_SRC_2X_ALPHA,
REPLACE_BLEND_2X_ALPHA,
REPLACE_BLEND_2X_SRC,
// Full blend equation runs in shader.
// We might have to make a copy of the framebuffer target to read from.
REPLACE_BLEND_COPY_FBO,
};
@@ -47,6 +54,9 @@ bool IsAlphaTestAgainstZero();
bool NeedsTestDiscard();
bool IsStencilTestOutputDisabled();
// If not, we have to emulate it in the shader, similar to blend replace.
bool IsColorMaskSimple(uint32_t colorMask);
StencilValueType ReplaceAlphaWithStencilType();
ReplaceAlphaType ReplaceAlphaWithStencil(ReplaceBlendType replaceBlend);
ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bufferFormat);
@@ -121,9 +131,9 @@ enum class BlendEq : uint8_t {
struct GenericBlendState {
bool enabled;
bool resetShaderBlending;
bool applyShaderBlending;
bool dirtyShaderBlend;
bool resetFramebufferRead;
bool applyFramebufferRead;
bool dirtyShaderBlendFixValues;
ReplaceAlphaType replaceAlphaWithStencil;
BlendFactor srcColor;
@@ -158,7 +168,16 @@ struct GenericBlendState {
};
void ConvertBlendState(GenericBlendState &blendState, bool allowShaderBlend);
void ApplyStencilReplaceAndLogicOp(ReplaceAlphaType replaceAlphaWithStencil, GenericBlendState &blendState);
void ApplyStencilReplaceAndLogicOpIgnoreBlend(ReplaceAlphaType replaceAlphaWithStencil, GenericBlendState &blendState);
struct GenericMaskState {
bool applyFramebufferRead;
uint32_t uniformMask; // For each bit, opposite to the PSP.
bool rgba[4]; // true = draw, false = don't draw this channel
};
void ConvertMaskState(GenericMaskState &maskState, bool allowFramebufferRead);
bool IsColorWriteMaskComplex(bool allowFramebufferRead);
struct GenericStencilFuncState {
bool enabled;
+19 -40
View File
@@ -33,7 +33,7 @@
#include "Core/HLE/sceDisplay.h"
#include "GPU/Common/PostShader.h"
#include "GPU/Common/PresentationCommon.h"
#include "GPU/Common/ShaderTranslation.h"
#include "Common/GPU/ShaderTranslation.h"
struct Vertex {
float x, y, z;
@@ -240,8 +240,8 @@ bool PresentationCommon::BuildPostShader(const ShaderInfo *shaderInfo, const Sha
}
std::string vsError, fsError;
Draw::ShaderModule *vs = CompileShaderModule(Draw::ShaderStage::VERTEX, GLSL_140, vsSourceGLSL, &vsError);
Draw::ShaderModule *fs = CompileShaderModule(Draw::ShaderStage::FRAGMENT, GLSL_140, fsSourceGLSL, &fsError);
Draw::ShaderModule *vs = CompileShaderModule(ShaderStage::Vertex, GLSL_1xx, vsSourceGLSL, &vsError);
Draw::ShaderModule *fs = CompileShaderModule(ShaderStage::Fragment, GLSL_1xx, fsSourceGLSL, &fsError);
// Don't worry, CompileShaderModule makes sure they get freed if one succeeded.
if (!fs || !vs) {
@@ -253,13 +253,13 @@ bool PresentationCommon::BuildPostShader(const ShaderInfo *shaderInfo, const Sha
return false;
}
Draw::UniformBufferDesc postShaderDesc{ sizeof(PostShaderUniforms), {
{ "gl_HalfPixel", 0, -1, Draw::UniformType::FLOAT4, offsetof(PostShaderUniforms, gl_HalfPixel) },
{ "u_texelDelta", 1, 1, Draw::UniformType::FLOAT2, offsetof(PostShaderUniforms, texelDelta) },
{ "u_pixelDelta", 2, 2, Draw::UniformType::FLOAT2, offsetof(PostShaderUniforms, pixelDelta) },
{ "u_time", 3, 3, Draw::UniformType::FLOAT4, offsetof(PostShaderUniforms, time) },
{ "u_setting", 4, 4, Draw::UniformType::FLOAT4, offsetof(PostShaderUniforms, setting) },
{ "u_video", 5, 5, Draw::UniformType::FLOAT1, offsetof(PostShaderUniforms, video) },
UniformBufferDesc postShaderDesc{ sizeof(PostShaderUniforms), {
{ "gl_HalfPixel", 0, -1, UniformType::FLOAT4, offsetof(PostShaderUniforms, gl_HalfPixel) },
{ "u_texelDelta", 1, 1, UniformType::FLOAT2, offsetof(PostShaderUniforms, texelDelta) },
{ "u_pixelDelta", 2, 2, UniformType::FLOAT2, offsetof(PostShaderUniforms, pixelDelta) },
{ "u_time", 3, 3, UniformType::FLOAT4, offsetof(PostShaderUniforms, time) },
{ "u_setting", 4, 4, UniformType::FLOAT4, offsetof(PostShaderUniforms, setting) },
{ "u_video", 5, 5, UniformType::FLOAT1, offsetof(PostShaderUniforms, video) },
} };
Draw::Pipeline *pipeline = CreatePipeline({ vs, fs }, true, &postShaderDesc);
if (!pipeline)
@@ -317,7 +317,7 @@ bool PresentationCommon::AllocateFramebuffer(int w, int h) {
}
// No depth/stencil for post processing
Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ w, h, 1, 1, false, Draw::FBO_8888, "presentation" });
Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ w, h, 1, 1, false, "presentation" });
if (!fbo) {
return false;
}
@@ -366,13 +366,13 @@ void PresentationCommon::DeviceRestore(Draw::DrawContext *draw) {
CreateDeviceObjects();
}
Draw::Pipeline *PresentationCommon::CreatePipeline(std::vector<Draw::ShaderModule *> shaders, bool postShader, const Draw::UniformBufferDesc *uniformDesc) {
Draw::Pipeline *PresentationCommon::CreatePipeline(std::vector<Draw::ShaderModule *> shaders, bool postShader, const UniformBufferDesc *uniformDesc) {
using namespace Draw;
Semantic pos = SEM_POSITION;
Semantic tc = SEM_TEXCOORD0;
// Shader translation marks these both as "TEXCOORDs" on HLSL...
if (postShader && (lang_ == HLSL_D3D11 || lang_ == HLSL_D3D11_LEVEL9 || lang_ == HLSL_DX9)) {
if (postShader && (lang_ == HLSL_D3D11 || lang_ == HLSL_D3D9)) {
pos = SEM_TEXCOORD0;
tc = SEM_TEXCOORD1;
}
@@ -411,7 +411,7 @@ void PresentationCommon::CreateDeviceObjects() {
vdata_ = draw_->CreateBuffer(sizeof(Vertex) * 8, BufferUsageFlag::DYNAMIC | BufferUsageFlag::VERTEXDATA);
// TODO: Use 4 and a strip?
// TODO: Use a triangle strip? Makes the UV rotation slightly more complex.
idata_ = draw_->CreateBuffer(sizeof(uint16_t) * 6, BufferUsageFlag::DYNAMIC | BufferUsageFlag::INDEXDATA);
uint16_t indexes[] = { 0, 1, 2, 0, 2, 3 };
draw_->UpdateBuffer(idata_, (const uint8_t *)indexes, 0, sizeof(indexes), Draw::UPDATE_DISCARD);
@@ -465,7 +465,7 @@ void PresentationCommon::DestroyPostShader() {
postShaderFBOUsage_.clear();
}
Draw::ShaderModule *PresentationCommon::CompileShaderModule(Draw::ShaderStage stage, ShaderLanguage lang, const std::string &src, std::string *errorString) {
Draw::ShaderModule *PresentationCommon::CompileShaderModule(ShaderStage stage, ShaderLanguage lang, const std::string &src, std::string *errorString) {
std::string translated = src;
bool translationFailed = false;
if (lang != lang_) {
@@ -476,30 +476,7 @@ Draw::ShaderModule *PresentationCommon::CompileShaderModule(Draw::ShaderStage st
}
}
Draw::ShaderLanguage mappedLang;
// These aren't exact, unfortunately, but we just need the type Draw will accept.
switch (lang_) {
case GLSL_140:
mappedLang = Draw::ShaderLanguage::GLSL_ES_200;
break;
case GLSL_300:
mappedLang = Draw::ShaderLanguage::GLSL_410;
break;
case GLSL_VULKAN:
mappedLang = Draw::ShaderLanguage::GLSL_VULKAN;
break;
case HLSL_DX9:
mappedLang = Draw::ShaderLanguage::HLSL_D3D9;
break;
case HLSL_D3D11:
case HLSL_D3D11_LEVEL9:
mappedLang = Draw::ShaderLanguage::HLSL_D3D11;
break;
default:
mappedLang = Draw::ShaderLanguage::GLSL_ES_200;
break;
}
Draw::ShaderModule *shader = draw_->CreateShaderModule(stage, mappedLang, (const uint8_t *)translated.c_str(), translated.size(), "postshader");
Draw::ShaderModule *shader = draw_->CreateShaderModule(stage, lang_, (const uint8_t *)translated.c_str(), translated.size(), "postshader");
if (shader)
postShaderModules_.push_back(shader);
return shader;
@@ -752,7 +729,7 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u
draw_->BindPipeline(nullptr);
}
void PresentationCommon::CalculateRenderResolution(int *width, int *height, bool *upscaling, bool *ssaa) {
void PresentationCommon::CalculateRenderResolution(int *width, int *height, int *scaleFactor, bool *upscaling, bool *ssaa) {
// Check if postprocessing shader is doing upscaling as it requires native resolution
std::vector<const ShaderInfo *> shaderInfo;
if (!g_Config.vPostShaderNames.empty() && g_Config.vPostShaderNames[0] != "Off") {
@@ -799,4 +776,6 @@ void PresentationCommon::CalculateRenderResolution(int *width, int *height, bool
*width = 480 * zoom;
*height = 272 * zoom;
}
*scaleFactor = zoom;
}
+4 -4
View File
@@ -17,7 +17,7 @@
#pragma once
#include "GPU/Common/ShaderCommon.h"
#include "Common/GPU/Shader.h"
struct CardboardSettings {
bool enabled;
@@ -100,7 +100,7 @@ public:
void SourceFramebuffer(Draw::Framebuffer *fb, int bufferWidth, int bufferHeight);
void CopyToOutput(OutputFlags flags, int uvRotation, float u0, float v0, float u1, float v1);
void CalculateRenderResolution(int *width, int *height, bool *upscaling, bool *ssaa);
void CalculateRenderResolution(int *width, int *height, int *scaleFactor, bool *upscaling, bool *ssaa);
protected:
void CreateDeviceObjects();
@@ -109,8 +109,8 @@ protected:
void ShowPostShaderError(const std::string &errorString);
Draw::ShaderModule *CompileShaderModule(Draw::ShaderStage stage, ShaderLanguage lang, const std::string &src, std::string *errorString);
Draw::Pipeline *CreatePipeline(std::vector<Draw::ShaderModule *> shaders, bool postShader, const Draw::UniformBufferDesc *uniformDesc);
Draw::ShaderModule *CompileShaderModule(ShaderStage stage, ShaderLanguage lang, const std::string &src, std::string *errorString);
Draw::Pipeline *CreatePipeline(std::vector<Draw::ShaderModule *> shaders, bool postShader, const UniformBufferDesc *uniformDesc);
bool BuildPostShader(const ShaderInfo *shaderInfo, const ShaderInfo *next);
bool AllocateFramebuffer(int w, int h);
+86
View File
@@ -0,0 +1,86 @@
#include <cstdarg>
#include "Common/GPU/Shader.h"
#include "Common/GPU/ShaderWriter.h"
#include "GPU/Common/ReinterpretFramebuffer.h"
static const VaryingDef varyings[1] = {
{ "vec2", "v_texcoord", "TEXCOORD0", 0, "highp" },
};
// TODO: We could possibly have an option to preserve any extra color precision? But gonna start without it.
// Requires full size integer math. It would be possible to make a floating point-only version with lots of
// modulo and stuff, might do it one day.
bool GenerateReinterpretFragmentShader(char *buffer, GEBufferFormat from, GEBufferFormat to, const ShaderLanguageDesc &lang) {
if (!lang.bitwiseOps) {
return false;
}
ShaderWriter writer(buffer, lang, ShaderStage::Fragment, nullptr, 0);
writer.HighPrecisionFloat();
writer.DeclareSampler2D("samp", 0);
writer.DeclareTexture2D("tex", 0);
writer.BeginFSMain(Slice<UniformDef>::empty(), varyings);
writer.C(" vec4 val = ").SampleTexture2D("tex", "samp", "v_texcoord.xy").C(";\n");
switch (from) {
case GE_FORMAT_4444:
writer.C(" uint color = uint(val.r * 15.99) | (uint(val.g * 15.99) << 4) | (uint(val.b * 15.99) << 8) | (uint(val.a * 15.99) << 12);\n");
break;
case GE_FORMAT_5551:
writer.C(" uint color = uint(val.r * 31.99) | (uint(val.g * 31.99) << 5) | (uint(val.b * 31.99) << 10);\n");
writer.C(" if (val.a >= 0.5) color |= 0x8000U;\n");
break;
case GE_FORMAT_565:
writer.C(" uint color = uint(val.r * 31.99) | (uint(val.g * 63.99) << 5) | (uint(val.b * 31.99) << 11);\n");
break;
default:
_assert_(false);
break;
}
switch (to) {
case GE_FORMAT_4444:
writer.C(" vec4 outColor = vec4(float(color & 0xFU), float((color >> 4) & 0xFU), float((color >> 8) & 0xFU), float((color >> 12) & 0xFU));\n");
writer.C(" outColor *= 1.0 / 15.0;\n");
break;
case GE_FORMAT_5551:
writer.C(" vec4 outColor = vec4(float(color & 0x1FU), float((color >> 5) & 0x1FU), float((color >> 10) & 0x1FU), 0.0);\n");
writer.C(" outColor.rgb *= 1.0 / 31.0;\n");
writer.C(" outColor.a = float(color >> 15);\n");
break;
case GE_FORMAT_565:
writer.C(" vec4 outColor = vec4(float(color & 0x1FU), float((color >> 5) & 0x3FU), float((color >> 11) & 0x1FU), 1.0);\n");
writer.C(" outColor.rb *= 1.0 / 31.0;\n");
writer.C(" outColor.g *= 1.0 / 63.0;\n");
break;
default:
_assert_(false);
break;
}
writer.EndFSMain("outColor");
return true;
}
bool GenerateReinterpretVertexShader(char *buffer, const ShaderLanguageDesc &lang) {
if (!lang.bitwiseOps) {
return false;
}
ShaderWriter writer(buffer, lang, ShaderStage::Vertex, nullptr, 0);
writer.BeginVSMain(Slice<InputDef>::empty(), Slice<UniformDef>::empty(), varyings);
writer.C(" float x = -1.0 + float((gl_VertexIndex & 1) << 2);\n");
writer.C(" float y = -1.0 + float((gl_VertexIndex & 2) << 1);\n");
writer.C(" v_texcoord = (vec2(x, y) + vec2(1.0, 1.0)) * 0.5;\n");
writer.F(" y *= %s1.0;\n", lang.viewportYSign);
writer.C(" gl_Position = vec4(x, y, 0.0, 1.0);\n");
writer.EndVSMain(varyings);
return true;
}
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include "Common/Log.h"
#include "GPU/ge_constants.h"
#include "GPU/GPUCommon.h"
#include "Common/GPU/ShaderWriter.h"
bool GenerateReinterpretFragmentShader(char *buffer, GEBufferFormat from, GEBufferFormat to, const ShaderLanguageDesc &lang);
// Just a single one. Can probably be shared with a lot of similar use cases.
// Generates the coordinates for a fullscreen triangle.
bool GenerateReinterpretVertexShader(char *buffer, const ShaderLanguageDesc &lang);
+2 -101
View File
@@ -1,101 +1,2 @@
#ifdef USE_CRT_DBG
#undef new
#endif
#include "ext/glslang/SPIRV/GlslangToSpv.h"
void init_resources(TBuiltInResource &Resources) {
Resources.maxLights = 32;
Resources.maxClipPlanes = 6;
Resources.maxTextureUnits = 32;
Resources.maxTextureCoords = 32;
Resources.maxVertexAttribs = 64;
Resources.maxVertexUniformComponents = 4096;
Resources.maxVaryingFloats = 64;
Resources.maxVertexTextureImageUnits = 32;
Resources.maxCombinedTextureImageUnits = 80;
Resources.maxTextureImageUnits = 32;
Resources.maxFragmentUniformComponents = 4096;
Resources.maxDrawBuffers = 32;
Resources.maxVertexUniformVectors = 128;
Resources.maxVaryingVectors = 8;
Resources.maxFragmentUniformVectors = 16;
Resources.maxVertexOutputVectors = 16;
Resources.maxFragmentInputVectors = 15;
Resources.minProgramTexelOffset = -8;
Resources.maxProgramTexelOffset = 7;
Resources.maxClipDistances = 8;
Resources.maxComputeWorkGroupCountX = 65535;
Resources.maxComputeWorkGroupCountY = 65535;
Resources.maxComputeWorkGroupCountZ = 65535;
Resources.maxComputeWorkGroupSizeX = 1024;
Resources.maxComputeWorkGroupSizeY = 1024;
Resources.maxComputeWorkGroupSizeZ = 64;
Resources.maxComputeUniformComponents = 1024;
Resources.maxComputeTextureImageUnits = 16;
Resources.maxComputeImageUniforms = 8;
Resources.maxComputeAtomicCounters = 8;
Resources.maxComputeAtomicCounterBuffers = 1;
Resources.maxVaryingComponents = 60;
Resources.maxVertexOutputComponents = 64;
Resources.maxGeometryInputComponents = 64;
Resources.maxGeometryOutputComponents = 128;
Resources.maxFragmentInputComponents = 128;
Resources.maxImageUnits = 8;
Resources.maxCombinedImageUnitsAndFragmentOutputs = 8;
Resources.maxCombinedShaderOutputResources = 8;
Resources.maxImageSamples = 0;
Resources.maxVertexImageUniforms = 0;
Resources.maxTessControlImageUniforms = 0;
Resources.maxTessEvaluationImageUniforms = 0;
Resources.maxGeometryImageUniforms = 0;
Resources.maxFragmentImageUniforms = 8;
Resources.maxCombinedImageUniforms = 8;
Resources.maxGeometryTextureImageUnits = 16;
Resources.maxGeometryOutputVertices = 256;
Resources.maxGeometryTotalOutputComponents = 1024;
Resources.maxGeometryUniformComponents = 1024;
Resources.maxGeometryVaryingComponents = 64;
Resources.maxTessControlInputComponents = 128;
Resources.maxTessControlOutputComponents = 128;
Resources.maxTessControlTextureImageUnits = 16;
Resources.maxTessControlUniformComponents = 1024;
Resources.maxTessControlTotalOutputComponents = 4096;
Resources.maxTessEvaluationInputComponents = 128;
Resources.maxTessEvaluationOutputComponents = 128;
Resources.maxTessEvaluationTextureImageUnits = 16;
Resources.maxTessEvaluationUniformComponents = 1024;
Resources.maxTessPatchComponents = 120;
Resources.maxPatchVertices = 32;
Resources.maxTessGenLevel = 64;
Resources.maxViewports = 16;
Resources.maxVertexAtomicCounters = 0;
Resources.maxTessControlAtomicCounters = 0;
Resources.maxTessEvaluationAtomicCounters = 0;
Resources.maxGeometryAtomicCounters = 0;
Resources.maxFragmentAtomicCounters = 8;
Resources.maxCombinedAtomicCounters = 8;
Resources.maxAtomicCounterBindings = 1;
Resources.maxVertexAtomicCounterBuffers = 0;
Resources.maxTessControlAtomicCounterBuffers = 0;
Resources.maxTessEvaluationAtomicCounterBuffers = 0;
Resources.maxGeometryAtomicCounterBuffers = 0;
Resources.maxFragmentAtomicCounterBuffers = 1;
Resources.maxCombinedAtomicCounterBuffers = 1;
Resources.maxAtomicCounterBufferSize = 16384;
Resources.maxTransformFeedbackBuffers = 4;
Resources.maxTransformFeedbackInterleavedComponents = 64;
Resources.maxCullDistances = 8;
Resources.maxCombinedClipAndCullDistances = 8;
Resources.maxSamples = 4;
Resources.limits.nonInductiveForLoops = 1;
Resources.limits.whileLoops = 1;
Resources.limits.doWhileLoops = 1;
Resources.limits.generalUniformIndexing = 1;
Resources.limits.generalAttributeMatrixVectorIndexing = 1;
Resources.limits.generalVaryingIndexing = 1;
Resources.limits.generalSamplerIndexing = 1;
Resources.limits.generalVariableIndexing = 1;
Resources.limits.generalConstantMatrixVectorIndexing = 1;
}
#include "Common/GPU/Shader.h"
#include "GPU/Common/ShaderCommon.h"
Loaded 100 of 257 files, more files were not shown because too many files have changed in this diff. Show more