Merge branch 'master' into feature_openxr_6dof

This commit is contained in:
Lubos 2022-07-31 16:37:54 +02:00
commit d129870d44
82 changed files with 2414 additions and 3306 deletions

View File

@ -5,6 +5,9 @@ on:
release:
types:
- created
push:
tags:
- "v*.*.*"
jobs:
build:
@ -19,7 +22,9 @@ jobs:
- name: archive
id: archive
run: |
VERSION=$(printf "%s\n" ${{ github.event.release.tag_name }} | sed 's/^v//')
VERSION=${GITHUB_REF##*/}
test -z "$VERSION" && VERSION=${{ github.event.release.tag_name }}
VERSION=$(printf "%s\n" "$VERSION" | sed 's/^v//')
PKGNAME="ppsspp-$VERSION"
mkdir -p /tmp/$PKGNAME
mv * /tmp/$PKGNAME
@ -38,11 +43,7 @@ jobs:
echo "::set-output name=tarball::$TARBALL"
- name: upload tarball
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: softprops/action-gh-release@v1
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: ./${{ steps.archive.outputs.tarball }}
asset_name: ${{ steps.archive.outputs.tarball }}
asset_content_type: application/x-xz
files: ${{ steps.archive.outputs.tarball }}
token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1458,8 +1458,6 @@ set(GPU_GLES
GPU/GLES/StencilBufferGLES.cpp
GPU/GLES/TextureCacheGLES.cpp
GPU/GLES/TextureCacheGLES.h
GPU/GLES/TextureScalerGLES.cpp
GPU/GLES/TextureScalerGLES.h
GPU/GLES/DrawEngineGLES.cpp
GPU/GLES/DrawEngineGLES.h
)
@ -1484,8 +1482,6 @@ set(GPU_VULKAN
GPU/Vulkan/StencilBufferVulkan.cpp
GPU/Vulkan/TextureCacheVulkan.cpp
GPU/Vulkan/TextureCacheVulkan.h
GPU/Vulkan/TextureScalerVulkan.cpp
GPU/Vulkan/TextureScalerVulkan.h
GPU/Vulkan/VulkanUtil.cpp
GPU/Vulkan/VulkanUtil.h
)
@ -1506,8 +1502,6 @@ set(GPU_D3D9
GPU/Directx9/StencilBufferDX9.cpp
GPU/Directx9/TextureCacheDX9.cpp
GPU/Directx9/TextureCacheDX9.h
GPU/Directx9/TextureScalerDX9.cpp
GPU/Directx9/TextureScalerDX9.h
)
set(GPU_D3D11
@ -1528,8 +1522,6 @@ set(GPU_D3D11
GPU/D3D11/StencilBufferD3D11.cpp
GPU/D3D11/TextureCacheD3D11.cpp
GPU/D3D11/TextureCacheD3D11.h
GPU/D3D11/TextureScalerD3D11.cpp
GPU/D3D11/TextureScalerD3D11.h
)
# We build Vulkan even on Apple to avoid annoying build differences.

View File

@ -13,6 +13,8 @@
#include "Common/Data/Encoding/Utf8.h"
#include "Common/Log.h"
#include <map>
#include <cfloat>
#include <D3Dcommon.h>
#include <d3d11.h>
@ -39,6 +41,24 @@ class D3D11SamplerState;
class D3D11RasterState;
class D3D11Framebuffer;
// This must stay POD for the memcmp to work reliably.
struct D3D11DepthStencilKey {
DepthStencilStateDesc desc;
u8 writeMask;
u8 compareMask;
bool operator < (const D3D11DepthStencilKey &other) const {
return memcmp(this, &other, sizeof(D3D11DepthStencilKey)) < 0;
}
};
class D3D11DepthStencilState : public DepthStencilState {
public:
~D3D11DepthStencilState() {}
DepthStencilStateDesc desc;
};
class D3D11DrawContext : public DrawContext {
public:
D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *deviceContext, ID3D11Device1 *device1, ID3D11DeviceContext1 *deviceContext1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd, std::vector<std::string> deviceList);
@ -102,9 +122,11 @@ public:
blendFactorDirty_ = true;
}
}
void SetStencilRef(uint8_t ref) override {
stencilRef_ = ref;
stencilRefDirty_ = true;
void SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) override {
stencilRef_ = refValue;
stencilWriteMask_ = writeMask;
stencilCompareMask_ = compareMask;
stencilDirty_ = true;
}
void EndFrame() override;
@ -174,6 +196,8 @@ public:
private:
void ApplyCurrentState();
ID3D11DepthStencilState *GetCachedDepthStencilState(D3D11DepthStencilState *state, uint8_t stencilWriteMask, uint8_t stencilCompareMask);
HWND hWnd_;
ID3D11Device *device_;
ID3D11DeviceContext *context_;
@ -200,8 +224,11 @@ private:
DeviceCaps caps_{};
AutoRef<D3D11BlendState> curBlend_;
AutoRef<D3D11DepthStencilState> curDepth_;
AutoRef<D3D11DepthStencilState> curDepthStencil_;
AutoRef<D3D11RasterState> curRaster_;
std::map<D3D11DepthStencilKey, ID3D11DepthStencilState *> depthStencilCache_;
ID3D11InputLayout *curInputLayout_ = nullptr;
ID3D11VertexShader *curVS_ = nullptr;
ID3D11PixelShader *curPS_ = nullptr;
@ -219,7 +246,9 @@ private:
float blendFactor_[4]{};
bool blendFactorDirty_ = false;
uint8_t stencilRef_ = 0;
bool stencilRefDirty_ = true;
uint8_t stencilWriteMask_ = 0xFF;
uint8_t stencilCompareMask_ = 0xFF;
bool stencilDirty_ = true;
// Temporaries
ID3D11Texture2D *packTexture_ = nullptr;
@ -415,14 +444,6 @@ void D3D11DrawContext::SetScissorRect(int left, int top, int width, int height)
context_->RSSetScissorRects(1, &rc);
}
class D3D11DepthStencilState : public DepthStencilState {
public:
~D3D11DepthStencilState() {
dss->Release();
}
ID3D11DepthStencilState *dss;
};
static const D3D11_COMPARISON_FUNC compareToD3D11[] = {
D3D11_COMPARISON_NEVER,
D3D11_COMPARISON_LESS,
@ -487,28 +508,13 @@ static D3D11_PRIMITIVE_TOPOLOGY primToD3D11[] = {
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ,
};
inline void CopyStencilSide(D3D11_DEPTH_STENCILOP_DESC &side, const StencilSide &input) {
inline void CopyStencilSide(D3D11_DEPTH_STENCILOP_DESC &side, const StencilSetup &input) {
side.StencilFunc = compareToD3D11[(int)input.compareOp];
side.StencilDepthFailOp = stencilOpToD3D11[(int)input.depthFailOp];
side.StencilFailOp = stencilOpToD3D11[(int)input.failOp];
side.StencilPassOp = stencilOpToD3D11[(int)input.passOp];
}
DepthStencilState *D3D11DrawContext::CreateDepthStencilState(const DepthStencilStateDesc &desc) {
D3D11DepthStencilState *ds = new D3D11DepthStencilState();
D3D11_DEPTH_STENCIL_DESC d3ddesc{};
d3ddesc.DepthEnable = desc.depthTestEnabled;
d3ddesc.DepthWriteMask = desc.depthWriteEnabled ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
d3ddesc.DepthFunc = compareToD3D11[(int)desc.depthCompare];
d3ddesc.StencilEnable = desc.stencilEnabled;
CopyStencilSide(d3ddesc.FrontFace, desc.front);
CopyStencilSide(d3ddesc.BackFace, desc.back);
if (SUCCEEDED(device_->CreateDepthStencilState(&d3ddesc, &ds->dss)))
return ds;
delete ds;
return nullptr;
}
static const D3D11_BLEND_OP blendOpToD3D11[] = {
D3D11_BLEND_OP_ADD,
D3D11_BLEND_OP_SUBTRACT,
@ -547,6 +553,46 @@ public:
float blendFactor[4];
};
ID3D11DepthStencilState *D3D11DrawContext::GetCachedDepthStencilState(D3D11DepthStencilState *state, uint8_t stencilWriteMask, uint8_t stencilCompareMask) {
D3D11DepthStencilKey key;
key.desc = state->desc;
key.writeMask = stencilWriteMask;
key.compareMask = stencilCompareMask;
auto findResult = depthStencilCache_.find(key);
if (findResult != depthStencilCache_.end()) {
return findResult->second;
}
// OK, create and insert.
D3D11_DEPTH_STENCIL_DESC d3ddesc{};
d3ddesc.DepthEnable = state->desc.depthTestEnabled;
d3ddesc.DepthWriteMask = state->desc.depthWriteEnabled ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
d3ddesc.DepthFunc = compareToD3D11[(int)state->desc.depthCompare];
d3ddesc.StencilEnable = state->desc.stencilEnabled;
d3ddesc.StencilReadMask = stencilCompareMask;
d3ddesc.StencilWriteMask = stencilWriteMask;
if (d3ddesc.StencilEnable) {
CopyStencilSide(d3ddesc.FrontFace, state->desc.stencil);
CopyStencilSide(d3ddesc.BackFace, state->desc.stencil);
}
ID3D11DepthStencilState *dss = nullptr;
if (SUCCEEDED(device_->CreateDepthStencilState(&d3ddesc, &dss))) {
depthStencilCache_[key] = dss;
return dss;
} else {
return nullptr;
}
}
DepthStencilState *D3D11DrawContext::CreateDepthStencilState(const DepthStencilStateDesc &desc) {
D3D11DepthStencilState *dss = new D3D11DepthStencilState();
dss->desc = desc;
return dynamic_cast<DepthStencilState *>(dss);
}
BlendState *D3D11DrawContext::CreateBlendState(const BlendStateDesc &desc) {
D3D11BlendState *bs = new D3D11BlendState();
D3D11_BLEND_DESC d3ddesc{};
@ -677,8 +723,7 @@ InputLayout *D3D11DrawContext::CreateInputLayout(const InputLayoutDesc &desc) {
class D3D11ShaderModule : public ShaderModule {
public:
D3D11ShaderModule(const std::string &tag) : tag_(tag) {
}
D3D11ShaderModule(const std::string &tag) : tag_(tag) { }
~D3D11ShaderModule() {
if (vs)
vs->Release();
@ -716,8 +761,11 @@ public:
AutoRef<D3D11InputLayout> input;
ID3D11InputLayout *il = nullptr;
AutoRef<D3D11BlendState> blend;
AutoRef<D3D11DepthStencilState> depth;
AutoRef<D3D11RasterState> raster;
// Combined with dynamic state to key into cached D3D11DepthStencilState, to emulate dynamic parameters.
AutoRef<D3D11DepthStencilState> depthStencil;
ID3D11VertexShader *vs = nullptr;
ID3D11PixelShader *ps = nullptr;
ID3D11GeometryShader *gs = nullptr;
@ -969,7 +1017,7 @@ ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLang
Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
D3D11Pipeline *dPipeline = new D3D11Pipeline();
dPipeline->blend = (D3D11BlendState *)desc.blend;
dPipeline->depth = (D3D11DepthStencilState *)desc.depthStencil;
dPipeline->depthStencil = (D3D11DepthStencilState *)desc.depthStencil;
dPipeline->input = (D3D11InputLayout *)desc.inputLayout;
dPipeline->raster = (D3D11RasterState *)desc.raster;
dPipeline->topology = primToD3D11[(int)desc.prim];
@ -977,8 +1025,9 @@ Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
dPipeline->dynamicUniformsSize = desc.uniformDesc->uniformBufferSize;
D3D11_BUFFER_DESC bufdesc{};
bufdesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
bufdesc.ByteWidth = (UINT)dPipeline->dynamicUniformsSize;
bufdesc.StructureByteStride = (UINT)dPipeline->dynamicUniformsSize;
// We just round up to 16 here. If we get some garbage, that's fine.
bufdesc.ByteWidth = ((UINT)dPipeline->dynamicUniformsSize + 15) & ~15;
bufdesc.StructureByteStride = bufdesc.ByteWidth;
bufdesc.Usage = D3D11_USAGE_DYNAMIC;
bufdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
HRESULT hr = device_->CreateBuffer(&bufdesc, nullptr, &dPipeline->dynamicUniforms);
@ -1042,7 +1091,7 @@ void D3D11DrawContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) {
void D3D11DrawContext::InvalidateCachedState() {
// This is a signal to forget all our state caching.
curBlend_ = nullptr;
curDepth_ = nullptr;
curDepthStencil_ = nullptr;
curRaster_ = nullptr;
curPS_ = nullptr;
curVS_ = nullptr;
@ -1065,10 +1114,11 @@ void D3D11DrawContext::ApplyCurrentState() {
curBlend_ = curPipeline_->blend;
blendFactorDirty_ = false;
}
if (curDepth_ != curPipeline_->depth || stencilRefDirty_) {
context_->OMSetDepthStencilState(curPipeline_->depth->dss, stencilRef_);
curDepth_ = curPipeline_->depth;
stencilRefDirty_ = false;
if (curDepthStencil_ != curPipeline_->depthStencil || stencilDirty_) {
ID3D11DepthStencilState *dss = GetCachedDepthStencilState(curPipeline_->depthStencil, stencilWriteMask_, stencilCompareMask_);
context_->OMSetDepthStencilState(dss, stencilRef_);
curDepthStencil_ = curPipeline_->depthStencil;
stencilDirty_ = false;
}
if (curRaster_ != curPipeline_->raster) {
context_->RSSetState(curPipeline_->raster->rs);
@ -1375,8 +1425,8 @@ void D3D11DrawContext::BeginFrame() {
if (curBlend_ != nullptr) {
context_->OMSetBlendState(curBlend_->bs, blendFactor_, 0xFFFFFFFF);
}
if (curDepth_ != nullptr) {
context_->OMSetDepthStencilState(curDepth_->dss, stencilRef_);
if (curDepthStencil_ != nullptr) {
context_->OMSetDepthStencilState(GetCachedDepthStencilState(curDepthStencil_, stencilWriteMask_, stencilCompareMask_), stencilRef_);
}
if (curRaster_ != nullptr) {
context_->RSSetState(curRaster_->rs);

View File

@ -50,9 +50,7 @@ void DirectXState::Restore() {
stencilTest.restore(); count++;
stencilOp.restore(); count++;
stencilFunc.restore(); count++;
stencilMask.restore(); count++;
dither.restore(); count++;
stencilWriteMask.restore(); count++;
texMinFilter.restore(); count++;
texMagFilter.restore(); count++;

View File

@ -288,66 +288,6 @@ private:
}
};
class SavedColorMask {
DWORD mask;
public:
SavedColorMask() {
mask = D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA;
DirectXState::state_count++;
}
inline void set(bool r, bool g, bool b, bool a) {
DWORD newmask = 0;
if (r) {
newmask |= D3DCOLORWRITEENABLE_RED;
}
if (g) {
newmask |= D3DCOLORWRITEENABLE_GREEN;
}
if (b) {
newmask |= D3DCOLORWRITEENABLE_BLUE;
}
if (a) {
newmask |= D3DCOLORWRITEENABLE_ALPHA;
}
if (mask != newmask) {
mask = newmask;
restore();
}
}
void force(bool r, bool g, bool b, bool a) {
DWORD old = mask;
set(r, g, b, a);
mask = old;
}
inline void restore() {
pD3Ddevice->SetRenderState(D3DRS_COLORWRITEENABLE, mask);
}
};
class BoolUnused {
public:
BoolUnused() {
DirectXState::state_count++;
}
inline void set(bool) {
// Nothing.
}
void force(bool) {
// Nothing.
}
inline void restore() {
// Nothing.
}
inline void enable() {
set(true);
}
inline void disable() {
set(false);
}
};
class StateVp {
D3DVIEWPORT9 viewport;
public:
@ -404,36 +344,6 @@ private:
}
};
class CullMode {
DWORD cull;
public:
CullMode() : cull (D3DCULL_NONE) {
}
inline void set(int wantcull, int cullmode) {
DWORD newcull;
if (!wantcull) {
// disable
newcull = D3DCULL_NONE;
} else {
// add front face ...
newcull = cullmode==0 ? D3DCULL_CW:D3DCULL_CCW;
}
if (cull != newcull) {
cull = newcull;
restore();
}
}
void force(int wantcull, int cullmode) {
DWORD old = cull;
set(wantcull, cullmode);
cull = old;
}
inline void restore() {
pD3Ddevice->SetRenderState(D3DRS_CULLMODE, cull);
}
};
bool initialized;
public:
@ -451,9 +361,7 @@ public:
BoolState<D3DRS_SCISSORTESTENABLE, false> scissorTest;
BoolUnused dither;
CullMode cullMode;
DxState1<D3DRS_CULLMODE, D3DCULL_NONE> cullMode;
DxState1<D3DRS_SHADEMODE, D3DSHADE_GOURAUD> shadeMode;
BoolState<D3DRS_ZENABLE, false> depthTest;
@ -465,7 +373,7 @@ public:
DxState1<D3DRS_ZFUNC, D3DCMP_LESSEQUAL> depthFunc;
DxState1<D3DRS_ZWRITEENABLE, TRUE> depthWrite;
SavedColorMask colorMask;
DxState1<D3DRS_COLORWRITEENABLE, 0xF> colorMask;
StateVp viewport;
StateScissor scissorRect;
@ -473,8 +381,10 @@ public:
BoolState<D3DRS_STENCILENABLE, false> stencilTest;
DxState3<D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP, D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP, D3DRS_STENCILPASS, D3DSTENCILOP_KEEP> stencilOp;
DxState3<D3DRS_STENCILFUNC, D3DCMP_ALWAYS, D3DRS_STENCILREF, 0, D3DRS_STENCILMASK, 0xFFFFFFFF> stencilFunc;
DxState1<D3DRS_STENCILWRITEMASK, 0xFFFFFFFF> stencilMask;
DxState1<D3DRS_STENCILFUNC, D3DCMP_ALWAYS> stencilFunc;
DxState1<D3DRS_STENCILREF, 0> stencilRef;
DxState1<D3DRS_STENCILWRITEMASK, 0xFFFFFFFF> stencilWriteMask;
DxState1<D3DRS_STENCILMASK, 0xFFFFFFFF> stencilCompareMask;
DxSampler0State1<D3DSAMP_MINFILTER, D3DTEXF_POINT> texMinFilter;
DxSampler0State1<D3DSAMP_MAGFILTER, D3DTEXF_POINT> texMagFilter;

View File

@ -162,33 +162,33 @@ public:
D3DSTENCILOP stencilZFail;
D3DSTENCILOP stencilPass;
D3DCMPFUNC stencilCompareOp;
uint8_t stencilCompareMask;
uint8_t stencilWriteMask;
void Apply(LPDIRECT3DDEVICE9 device) {
device->SetRenderState(D3DRS_ZENABLE, depthTestEnabled);
void Apply(LPDIRECT3DDEVICE9 device, uint8_t stencilRef, uint8_t stencilWriteMask, uint8_t stencilCompareMask) {
using namespace DX9;
dxstate.depthTest.set(depthTestEnabled);
if (depthTestEnabled) {
device->SetRenderState(D3DRS_ZWRITEENABLE, depthWriteEnabled);
device->SetRenderState(D3DRS_ZFUNC, depthCompare);
dxstate.depthWrite.set(depthWriteEnabled);
dxstate.depthFunc.set(depthCompare);
}
device->SetRenderState(D3DRS_STENCILENABLE, stencilEnabled);
dxstate.stencilTest.set(stencilEnabled);
if (stencilEnabled) {
device->SetRenderState(D3DRS_STENCILFAIL, stencilFail);
device->SetRenderState(D3DRS_STENCILZFAIL, stencilZFail);
device->SetRenderState(D3DRS_STENCILPASS, stencilPass);
device->SetRenderState(D3DRS_STENCILFUNC, stencilCompareOp);
device->SetRenderState(D3DRS_STENCILMASK, stencilCompareMask);
device->SetRenderState(D3DRS_STENCILWRITEMASK, stencilWriteMask);
dxstate.stencilOp.set(stencilFail, stencilZFail, stencilPass);
dxstate.stencilFunc.set(stencilCompareOp);
dxstate.stencilRef.set(stencilRef);
dxstate.stencilCompareMask.set(stencilCompareMask);
dxstate.stencilWriteMask.set(stencilWriteMask);
}
}
};
class D3D9RasterState : public RasterState {
public:
DWORD cullMode;
DWORD cullMode; // D3DCULL_*
void Apply(LPDIRECT3DDEVICE9 device) {
device->SetRenderState(D3DRS_CULLMODE, cullMode);
device->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
using namespace DX9;
dxstate.cullMode.set(cullMode);
dxstate.scissorTest.enable();
}
};
@ -197,19 +197,14 @@ public:
bool enabled;
D3DBLENDOP eqCol, eqAlpha;
D3DBLEND srcCol, srcAlpha, dstCol, dstAlpha;
uint32_t fixedColor;
uint32_t colorMask;
void Apply(LPDIRECT3DDEVICE9 device) {
device->SetRenderState(D3DRS_ALPHABLENDENABLE, (DWORD)enabled);
device->SetRenderState(D3DRS_BLENDOP, eqCol);
device->SetRenderState(D3DRS_BLENDOPALPHA, eqAlpha);
device->SetRenderState(D3DRS_SRCBLEND, srcCol);
device->SetRenderState(D3DRS_DESTBLEND, dstCol);
device->SetRenderState(D3DRS_SRCBLENDALPHA, srcAlpha);
device->SetRenderState(D3DRS_DESTBLENDALPHA, dstAlpha);
device->SetRenderState(D3DRS_COLORWRITEENABLE, colorMask);
// device->SetRenderState(, fixedColor);
using namespace DX9;
dxstate.blend.set(enabled);
dxstate.blendFunc.set(srcCol, dstCol, srcAlpha, dstAlpha);
dxstate.blendEquation.set(eqCol, eqAlpha);
dxstate.colorMask.set(colorMask);
}
};
@ -219,11 +214,12 @@ public:
D3DTEXTUREFILTERTYPE magFilt, minFilt, mipFilt;
void Apply(LPDIRECT3DDEVICE9 device, int index) {
device->SetSamplerState(index, D3DSAMP_ADDRESSU, wrapS);
device->SetSamplerState(index, D3DSAMP_ADDRESSV, wrapT);
device->SetSamplerState(index, D3DSAMP_MAGFILTER, magFilt);
device->SetSamplerState(index, D3DSAMP_MINFILTER, minFilt);
device->SetSamplerState(index, D3DSAMP_MIPFILTER, mipFilt);
using namespace DX9;
dxstate.texAddressU.set(wrapS);
dxstate.texAddressV.set(wrapT);
dxstate.texMagFilter.set(magFilt);
dxstate.texMinFilter.set(minFilt);
dxstate.texMipFilter.set(mipFilt);
}
};
@ -291,7 +287,7 @@ public:
AutoRef<D3D9RasterState> raster;
UniformBufferDesc dynamicUniforms;
void Apply(LPDIRECT3DDEVICE9 device);
void Apply(LPDIRECT3DDEVICE9 device, uint8_t stencilRef, uint8_t stencilWriteMask, uint8_t stencilCompareMask);
};
class D3D9Texture : public Texture {
@ -558,8 +554,9 @@ public:
void SetScissorRect(int left, int top, int width, int height) override;
void SetViewports(int count, Viewport *viewports) override;
void SetBlendFactor(float color[4]) override;
void SetStencilRef(uint8_t ref) override;
void SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) override;
void ApplyDynamicState();
void Draw(int vertexCount, int offset) override;
void DrawIndexed(int vertexCount, int offset) override;
void DrawUP(const void *vdata, int vertexCount) override;
@ -618,10 +615,17 @@ private:
int curIBufferOffset_ = 0;
AutoRef<Framebuffer> curRenderTarget_;
u8 stencilRefValue_ = 0;
u8 stencilCompareMask_ = 0xFF;
u8 stencilWriteMask_ = 0xFF;
// Framebuffer state
LPDIRECT3DSURFACE9 deviceRTsurf = 0;
LPDIRECT3DSURFACE9 deviceDSsurf = 0;
bool supportsINTZ = false;
// Dynamic state
uint8_t stencilRef_ = 0;
};
void D3D9Context::InvalidateCachedState() {
@ -674,6 +678,8 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID
}
shaderLanguageDesc_.Init(HLSL_D3D9);
DX9::dxstate.Restore();
}
D3D9Context::~D3D9Context() {
@ -725,12 +731,10 @@ DepthStencilState *D3D9Context::CreateDepthStencilState(const DepthStencilStateD
ds->depthWriteEnabled = desc.depthWriteEnabled;
ds->depthCompare = compareToD3D9[(int)desc.depthCompare];
ds->stencilEnabled = desc.stencilEnabled;
ds->stencilCompareOp = compareToD3D9[(int)desc.front.compareOp];
ds->stencilPass = stencilOpToD3D9[(int)desc.front.passOp];
ds->stencilFail = stencilOpToD3D9[(int)desc.front.failOp];
ds->stencilZFail = stencilOpToD3D9[(int)desc.front.depthFailOp];
ds->stencilWriteMask = desc.front.writeMask;
ds->stencilCompareMask = desc.front.compareMask;
ds->stencilCompareOp = compareToD3D9[(int)desc.stencil.compareOp];
ds->stencilPass = stencilOpToD3D9[(int)desc.stencil.passOp];
ds->stencilFail = stencilOpToD3D9[(int)desc.stencil.failOp];
ds->stencilZFail = stencilOpToD3D9[(int)desc.stencil.depthFailOp];
return ds;
}
@ -951,32 +955,44 @@ void D3D9Context::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offse
}
}
void D3D9Pipeline::Apply(LPDIRECT3DDEVICE9 device) {
void D3D9Pipeline::Apply(LPDIRECT3DDEVICE9 device, uint8_t stencilRef, uint8_t stencilWriteMask, uint8_t stencilCompareMask) {
vshader->Apply(device);
pshader->Apply(device);
blend->Apply(device);
depthStencil->Apply(device);
depthStencil->Apply(device, stencilRef, stencilWriteMask, stencilCompareMask);
raster->Apply(device);
}
void D3D9Context::ApplyDynamicState() {
// Apply dynamic state.
if (curPipeline_->depthStencil->stencilEnabled) {
device_->SetRenderState(D3DRS_STENCILREF, (DWORD)stencilRefValue_);
device_->SetRenderState(D3DRS_STENCILWRITEMASK, (DWORD)stencilWriteMask_);
device_->SetRenderState(D3DRS_STENCILMASK, (DWORD)stencilCompareMask_);
}
}
void D3D9Context::Draw(int vertexCount, int offset) {
device_->SetStreamSource(0, curVBuffers_[0]->vbuffer_, curVBufferOffsets_[0], curPipeline_->inputLayout->GetStride(0));
curPipeline_->Apply(device_);
curPipeline_->inputLayout->Apply(device_);
curPipeline_->Apply(device_, stencilRef_, stencilWriteMask_, stencilCompareMask_);
ApplyDynamicState();
device_->DrawPrimitive(curPipeline_->prim, offset, vertexCount / 3);
}
void D3D9Context::DrawIndexed(int vertexCount, int offset) {
curPipeline_->Apply(device_);
curPipeline_->inputLayout->Apply(device_);
curPipeline_->Apply(device_, stencilRef_, stencilWriteMask_, stencilCompareMask_);
ApplyDynamicState();
device_->SetStreamSource(0, curVBuffers_[0]->vbuffer_, curVBufferOffsets_[0], curPipeline_->inputLayout->GetStride(0));
device_->SetIndices(curIBuffer_->ibuffer_);
device_->DrawIndexedPrimitive(curPipeline_->prim, 0, 0, vertexCount, offset, vertexCount / curPipeline_->primDivisor);
}
void D3D9Context::DrawUP(const void *vdata, int vertexCount) {
curPipeline_->Apply(device_);
curPipeline_->inputLayout->Apply(device_);
curPipeline_->Apply(device_, stencilRef_, stencilWriteMask_, stencilCompareMask_);
ApplyDynamicState();
device_->DrawPrimitiveUP(curPipeline_->prim, vertexCount / 3, vdata, curPipeline_->inputLayout->GetStride(0));
}
@ -998,6 +1014,7 @@ void D3D9Context::SetScissorRect(int left, int top, int width, int height) {
using namespace DX9;
dxstate.scissorRect.set(left, top, left + width, top + height);
dxstate.scissorTest.set(true);
}
void D3D9Context::SetViewports(int count, Viewport *viewports) {
@ -1015,11 +1032,14 @@ void D3D9Context::SetBlendFactor(float color[4]) {
uint32_t g = (uint32_t)(color[1] * 255.0f);
uint32_t b = (uint32_t)(color[2] * 255.0f);
uint32_t a = (uint32_t)(color[3] * 255.0f);
device_->SetRenderState(D3DRS_BLENDFACTOR, r | (g << 8) | (b << 16) | (a << 24));
using namespace DX9;
dxstate.blendColor.set(color);
}
void D3D9Context::SetStencilRef(uint8_t ref) {
device_->SetRenderState(D3DRS_STENCILREF, (DWORD)ref);
void D3D9Context::SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) {
stencilRefValue_ = refValue;
stencilWriteMask_ = writeMask;
stencilCompareMask_ = compareMask;
}
bool D3D9ShaderModule::Compile(LPDIRECT3DDEVICE9 device, const uint8_t *data, size_t size) {

View File

@ -381,9 +381,9 @@ void GLQueueRunner::RunInitSteps(const std::vector<GLRInitStep> &steps, bool ski
boundTexture = tex->texture;
}
if (!gl_extensions.IsGLES || gl_extensions.GLES3) {
glTexParameteri(tex->target, GL_TEXTURE_MAX_LEVEL, step.texture_finalize.maxLevel);
glTexParameteri(tex->target, GL_TEXTURE_MAX_LEVEL, step.texture_finalize.loadedLevels - 1);
}
tex->maxLod = (float)step.texture_finalize.maxLevel;
tex->maxLod = (float)step.texture_finalize.loadedLevels - 1;
if (step.texture_finalize.genMips) {
glGenerateMipmap(tex->target);
}

View File

@ -267,7 +267,7 @@ struct GLRInitStep {
} texture_image;
struct {
GLRTexture *texture;
int maxLevel;
int loadedLevels;
bool genMips;
} texture_finalize;
};

View File

@ -565,10 +565,10 @@ public:
curRenderStep_->commands.push_back(_data);
}
void FinalizeTexture(GLRTexture *texture, int maxLevels, bool genMips) {
void FinalizeTexture(GLRTexture *texture, int loadedLevels, bool genMips) {
GLRInitStep step{ GLRInitStepType::TEXTURE_FINALIZE };
step.texture_finalize.texture = texture;
step.texture_finalize.maxLevel = maxLevels;
step.texture_finalize.loadedLevels = loadedLevels;
step.texture_finalize.genMips = genMips;
initSteps_.push_back(step);
}

View File

@ -175,10 +175,8 @@ public:
GLuint stencilZFail;
GLuint stencilPass;
GLuint stencilCompareOp;
uint8_t stencilCompareMask;
uint8_t stencilWriteMask;
void Apply(GLRenderManager *render, uint8_t stencilRef) {
void Apply(GLRenderManager *render, uint8_t stencilRef, uint8_t stencilWriteMask, uint8_t stencilCompareMask) {
render->SetDepth(depthTestEnabled, depthWriteEnabled, depthComp);
render->SetStencilFunc(stencilEnabled, stencilCompareOp, stencilRef, stencilCompareMask);
render->SetStencilOp(stencilWriteMask, stencilFail, stencilZFail, stencilPass);
@ -388,13 +386,21 @@ public:
renderManager_.SetBlendFactor(color);
}
void SetStencilRef(uint8_t ref) override {
stencilRef_ = ref;
void SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) override {
stencilRef_ = refValue;
stencilWriteMask_ = writeMask;
stencilCompareMask_ = compareMask;
// Do we need to update on the fly here?
renderManager_.SetStencilFunc(
curPipeline_->depthStencil->stencilEnabled,
curPipeline_->depthStencil->stencilCompareOp,
ref,
curPipeline_->depthStencil->stencilCompareMask);
refValue,
compareMask);
renderManager_.SetStencilOp(
writeMask,
curPipeline_->depthStencil->stencilFail,
curPipeline_->depthStencil->stencilZFail,
curPipeline_->depthStencil->stencilPass);
}
void BindTextures(int start, int count, Texture **textures) override;
@ -491,6 +497,8 @@ private:
AutoRef<Framebuffer> curRenderTarget_;
uint8_t stencilRef_ = 0;
uint8_t stencilWriteMask_ = 0;
uint8_t stencilCompareMask_ = 0;
// Frames in flight is not such a strict concept as with Vulkan until we start using glBufferStorage and fences.
// But might as well have the structure ready, and can't hurt to rotate buffers.
@ -936,12 +944,10 @@ DepthStencilState *OpenGLContext::CreateDepthStencilState(const DepthStencilStat
ds->depthWriteEnabled = desc.depthWriteEnabled;
ds->depthComp = compToGL[(int)desc.depthCompare];
ds->stencilEnabled = desc.stencilEnabled;
ds->stencilCompareOp = compToGL[(int)desc.front.compareOp];
ds->stencilPass = stencilOpToGL[(int)desc.front.passOp];
ds->stencilFail = stencilOpToGL[(int)desc.front.failOp];
ds->stencilZFail = stencilOpToGL[(int)desc.front.depthFailOp];
ds->stencilWriteMask = desc.front.writeMask;
ds->stencilCompareMask = desc.front.compareMask;
ds->stencilCompareOp = compToGL[(int)desc.stencil.compareOp];
ds->stencilPass = stencilOpToGL[(int)desc.stencil.passOp];
ds->stencilFail = stencilOpToGL[(int)desc.stencil.failOp];
ds->stencilZFail = stencilOpToGL[(int)desc.stencil.depthFailOp];
return ds;
}
@ -1185,7 +1191,7 @@ void OpenGLContext::BindPipeline(Pipeline *pipeline) {
return;
}
curPipeline_->blend->Apply(&renderManager_);
curPipeline_->depthStencil->Apply(&renderManager_, stencilRef_);
curPipeline_->depthStencil->Apply(&renderManager_, stencilRef_, stencilWriteMask_, stencilCompareMask_);
curPipeline_->raster->Apply(&renderManager_);
renderManager_.BindProgram(curPipeline_->program_);
}

View File

@ -155,6 +155,7 @@ void VulkanTexture::ClearMip(VkCommandBuffer cmd, int mip, uint32_t value) {
// Low-quality mipmap generation by bilinear blit, but works okay.
void VulkanTexture::GenerateMips(VkCommandBuffer cmd, int firstMipToGenerate, bool fromCompute) {
_assert_msg_(firstMipToGenerate > 0, "Cannot generate the first level");
_assert_msg_(firstMipToGenerate < numMips_, "Can't generate levels beyond storage");
// Transition the pre-set levels to GENERAL.
TransitionImageLayout2(cmd, image_, 0, firstMipToGenerate, VK_IMAGE_ASPECT_COLOR_BIT,

View File

@ -283,8 +283,6 @@ public:
int dynamicUniformSize = 0;
bool usesStencil = false;
uint8_t stencilWriteMask = 0xFF;
uint8_t stencilTestMask = 0xFF;
private:
VulkanContext *vulkan_;
@ -407,7 +405,7 @@ public:
void SetScissorRect(int left, int top, int width, int height) override;
void SetViewports(int count, Viewport *viewports) override;
void SetBlendFactor(float color[4]) override;
void SetStencilRef(uint8_t stencilRef) override;
void SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) override;
void BindSamplerStates(int start, int count, SamplerState **state) override;
void BindTextures(int start, int count, Texture **textures) override;
@ -554,6 +552,8 @@ private:
DeviceCaps caps_{};
uint8_t stencilRef_ = 0;
uint8_t stencilWriteMask_ = 0xFF;
uint8_t stencilCompareMask_ = 0xFF;
};
static int GetBpp(VkFormat format) {
@ -1131,8 +1131,6 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
}
if (depth->info.stencilTestEnable) {
pipeline->usesStencil = true;
pipeline->stencilTestMask = depth->info.front.compareMask;
pipeline->stencilWriteMask = depth->info.front.writeMask;
}
return pipeline;
}
@ -1160,10 +1158,12 @@ void VKContext::SetBlendFactor(float color[4]) {
renderManager_.SetBlendFactor(col);
}
void VKContext::SetStencilRef(uint8_t stencilRef) {
void VKContext::SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) {
if (curPipeline_->usesStencil)
renderManager_.SetStencilParams(curPipeline_->stencilWriteMask, curPipeline_->stencilTestMask, stencilRef);
stencilRef_ = stencilRef;
renderManager_.SetStencilParams(writeMask, compareMask, refValue);
stencilRef_ = refValue;
stencilWriteMask_ = writeMask;
stencilCompareMask_ = compareMask;
}
InputLayout *VKContext::CreateInputLayout(const InputLayoutDesc &desc) {
@ -1207,9 +1207,7 @@ Texture *VKContext::CreateTexture(const TextureDesc &desc) {
}
}
static inline void CopySide(VkStencilOpState &dest, const StencilSide &src) {
dest.compareMask = src.compareMask;
dest.writeMask = src.writeMask;
static inline void CopySide(VkStencilOpState &dest, const StencilSetup &src) {
dest.compareOp = compToVK[(int)src.compareOp];
dest.failOp = stencilOpToVK[(int)src.failOp];
dest.passOp = stencilOpToVK[(int)src.passOp];
@ -1224,8 +1222,8 @@ DepthStencilState *VKContext::CreateDepthStencilState(const DepthStencilStateDes
ds->info.stencilTestEnable = desc.stencilEnabled;
ds->info.depthBoundsTestEnable = false;
if (ds->info.stencilTestEnable) {
CopySide(ds->info.front, desc.front);
CopySide(ds->info.back, desc.back);
CopySide(ds->info.front, desc.stencil);
CopySide(ds->info.back, desc.stencil);
}
return ds;
}
@ -1313,7 +1311,7 @@ void VKContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) {
void VKContext::ApplyDynamicState() {
// TODO: blend constants, stencil, viewports should be here, after bindpipeline..
if (curPipeline_->usesStencil) {
renderManager_.SetStencilParams(curPipeline_->stencilWriteMask, curPipeline_->stencilTestMask, stencilRef_);
renderManager_.SetStencilParams(stencilWriteMask_, stencilCompareMask_, stencilRef_);
}
}

View File

@ -453,13 +453,11 @@ public:
class RasterState : public RefCountedObject {};
struct StencilSide {
struct StencilSetup {
StencilOp failOp;
StencilOp passOp;
StencilOp depthFailOp;
Comparison compareOp;
uint8_t compareMask;
uint8_t writeMask;
};
struct DepthStencilStateDesc {
@ -467,8 +465,7 @@ struct DepthStencilStateDesc {
bool depthWriteEnabled;
Comparison depthCompare;
bool stencilEnabled;
StencilSide front;
StencilSide back;
StencilSetup stencil;
};
struct BlendStateDesc {
@ -652,7 +649,7 @@ public:
virtual void SetScissorRect(int left, int top, int width, int height) = 0;
virtual void SetViewports(int count, Viewport *viewports) = 0;
virtual void SetBlendFactor(float color[4]) = 0;
virtual void SetStencilRef(uint8_t ref) = 0;
virtual void SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) = 0;
virtual void BindSamplerStates(int start, int count, SamplerState **state) = 0;
virtual void BindTextures(int start, int count, Texture **textures) = 0;

View File

@ -77,6 +77,12 @@ std::string TextDrawerAndroid::NormalizeString(std::string str) {
}
void TextDrawerAndroid::MeasureString(const char *str, size_t len, float *w, float *h) {
if (!str) {
*w = 0.0;
*h = 0.0;
return;
}
CacheKey key{ std::string(str, len), fontHash_ };
TextMeasureEntry *entry;
auto iter = sizeCache_.find(key);

View File

@ -24,6 +24,7 @@
#include "Core/HLE/sceKernelThread.h"
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSDebugInterface.h"
#include "Core/Reporting.h"
DebuggerSubscriber *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map) {
// No need to bind or alloc state, these are all global.
@ -365,6 +366,8 @@ void WebSocketCPUSetReg(DebuggerRequest &req) {
return;
}
Reporting::NotifyDebugger();
JsonWriter &json = req.Respond();
// Repeat it back just to avoid confusion on how it parsed.
json.writeInt("category", cat);

View File

@ -29,6 +29,7 @@
#include "Core/MemMap.h"
#include "Core/MIPS/MIPSAsm.h"
#include "Core/MIPS/MIPSDebugInterface.h"
#include "Core/Reporting.h"
class WebSocketDisasmState : public DebuggerSubscriber {
public:
@ -261,6 +262,7 @@ void WebSocketDisasmState::WriteBranchGuide(JsonWriter &json, const BranchLine &
// - addressHex: string indicating base address in hexadecimal (may be 64 bit.)
void WebSocketDisasmState::Base(DebuggerRequest &req) {
JsonWriter &json = req.Respond();
Reporting::NotifyDebugger();
json.writeString("addressHex", StringFromFormat("%016llx", (uintptr_t)Memory::base));
}
@ -483,5 +485,6 @@ void WebSocketDisasmState::Assemble(DebuggerRequest &req) {
return req.Fail(StringFromFormat("Could not assemble: %s", ConvertWStringToUTF8(MIPSAsm::GetAssembleError()).c_str()));
JsonWriter &json = req.Respond();
Reporting::NotifyDebugger();
json.writeUint("encoding", Memory::Read_Instruction(address).encoding);
}

View File

@ -26,6 +26,7 @@
#include "Core/MIPS/MIPSDebugInterface.h"
#include "Core/MIPS/MIPSStackWalk.h"
#include "Core/HLE/sceKernelThread.h"
#include "Core/Reporting.h"
DebuggerSubscriber *WebSocketHLEInit(DebuggerEventHandlerMap &map) {
map["hle.thread.list"] = &WebSocketHLEThreadList;
@ -146,6 +147,8 @@ void WebSocketHLEThreadWake(DebuggerRequest &req) {
return req.Fail("Cannot force run thread based on current status");
}
Reporting::NotifyDebugger();
JsonWriter &json = req.Respond();
json.writeUint("thread", threadInfo.id);
json.writeString("status", "ready");
@ -182,6 +185,8 @@ void WebSocketHLEThreadStop(DebuggerRequest &req) {
if ((threadInfo.status & THREADSTATUS_DORMANT) == 0)
return req.Fail("Failed to stop thread");
Reporting::NotifyDebugger();
JsonWriter &json = req.Respond();
json.writeUint("thread", threadInfo.id);
json.writeString("status", "dormant");

View File

@ -21,12 +21,13 @@
#include "Common/Data/Encoding/Base64.h"
#include "Common/StringUtils.h"
#include "Core/Core.h"
#include "Core/Debugger/WebSocket/MemorySubscriber.h"
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
#include "Core/HLE/ReplaceTables.h"
#include "Core/MemMap.h"
#include "Core/MIPS/MIPSDebugInterface.h"
#include "Core/Reporting.h"
#include "Core/System.h"
#include "Core/Debugger/WebSocket/MemorySubscriber.h"
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
DebuggerSubscriber *WebSocketMemoryInit(DebuggerEventHandlerMap &map) {
// No need to bind or alloc state, these are all global.
@ -279,6 +280,7 @@ void WebSocketMemoryWriteU8(DebuggerRequest &req) {
}
currentMIPS->InvalidateICache(addr, 1);
Memory::Write_U8(val, addr);
Reporting::NotifyDebugger();
JsonWriter &json = req.Respond();
json.writeUint("value", Memory::Read_U8(addr));
@ -311,6 +313,7 @@ void WebSocketMemoryWriteU16(DebuggerRequest &req) {
}
currentMIPS->InvalidateICache(addr, 2);
Memory::Write_U16(val, addr);
Reporting::NotifyDebugger();
JsonWriter &json = req.Respond();
json.writeUint("value", Memory::Read_U16(addr));
@ -343,6 +346,7 @@ void WebSocketMemoryWriteU32(DebuggerRequest &req) {
}
currentMIPS->InvalidateICache(addr, 4);
Memory::Write_U32(val, addr);
Reporting::NotifyDebugger();
JsonWriter &json = req.Respond();
json.writeUint("value", Memory::Read_U32(addr));
@ -377,5 +381,6 @@ void WebSocketMemoryWrite(DebuggerRequest &req) {
currentMIPS->InvalidateICache(addr, size);
Memory::MemcpyUnchecked(addr, &value[0], size);
Reporting::NotifyDebugger();
req.Respond();
}

View File

@ -231,7 +231,7 @@ bool CISOFileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr, bool uncached)
const u32 idx = index[frameNumber];
const u32 indexPos = idx & 0x7FFFFFFF;
const u32 nextIndexPos = index[frameNumber + 1] & 0x7FFFFFFF;
z_stream z;
z_stream z{};
const u64 compressedReadPos = (u64)indexPos << indexShift;
const u64 compressedReadEnd = (u64)nextIndexPos << indexShift;
@ -311,10 +311,7 @@ bool CISOFileBlockDevice::ReadBlocks(u32 minBlock, int count, u8 *outPtr) {
const u32 afterLastIndexPos = index[lastFrameNumber + 1] & 0x7FFFFFFF;
const u64 totalReadEnd = (u64)afterLastIndexPos << indexShift;
z_stream z;
z.zalloc = Z_NULL;
z.zfree = Z_NULL;
z.opaque = Z_NULL;
z_stream z{};
if (inflateInit2(&z, -15) != Z_OK) {
ERROR_LOG(LOADER, "Unable to initialize inflate: %s\n", (z.msg) ? z.msg : "?");
return false;

View File

@ -353,7 +353,8 @@ int sceKernelRegisterDefaultExceptionHandler()
void sceKernelSetGPO(u32 ledAddr)
{
// Sets debug LEDs.
DEBUG_LOG(SCEKERNEL, "sceKernelSetGPO(%02x)", ledAddr);
// Not really interesting, and a few games really spam it
// DEBUG_LOG(SCEKERNEL, "sceKernelSetGPO(%02x)", ledAddr);
}
u32 sceKernelGetGPI()

View File

@ -747,7 +747,7 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts
return logBlocks;
}
bool IRReadsFromGPR(const IRInst &inst, int reg) {
bool IRReadsFromGPR(const IRInst &inst, int reg, bool directly = false) {
const IRMeta *m = GetIRMeta(inst.op);
if (m->types[1] == 'G' && inst.src1 == reg) {
@ -759,8 +759,11 @@ bool IRReadsFromGPR(const IRInst &inst, int reg) {
if ((m->flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0 && m->types[0] == 'G' && inst.src3 == reg) {
return true;
}
if (inst.op == IROp::Interpret || inst.op == IROp::CallReplacement) {
return true;
if (!directly) {
if (inst.op == IROp::Interpret || inst.op == IROp::CallReplacement || inst.op == IROp::Syscall || inst.op == IROp::Break)
return true;
if (inst.op == IROp::Breakpoint || inst.op == IROp::MemoryCheck)
return true;
}
return false;
}
@ -810,16 +813,22 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
std::vector<IRInst> insts;
insts.reserve(in.GetInstructions().size());
// We track writes both to rename regs and to purge dead stores.
struct Check {
Check(int r, int i, bool rbx) : reg(r), index(i), readByExit(rbx) {
}
// Register this instruction wrote to.
int reg;
// Only other than -1 when it's a Mov, equivalent reg at this point.
int srcReg = -1;
// Index into insts for this op.
int index;
// Whether the dest reg is read by any Exit.
bool readByExit;
};
std::vector<Check> checks;
// This tracks the last index at which each reg was modified.
int lastWrittenTo[256];
memset(lastWrittenTo, -1, sizeof(lastWrittenTo));
@ -828,23 +837,43 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
IRInst inst = in.GetInstructions()[i];
const IRMeta *m = GetIRMeta(inst.op);
// Check if we can optimize by running through all the writes we've previously found.
for (Check &check : checks) {
if (check.reg == 0) {
// This means we already optimized this or a later inst depends on it.
continue;
}
if (IRReadsFromGPR(inst, check.reg)) {
// Read from, but was this just a copy?
// If this reads from the reg, we either depend on it or we can fold or swap.
// That's determined below.
// If this reads and writes the reg (e.g. MovZ, Load32Left), we can't just swap.
bool mutatesReg = IRMutatesDestGPR(inst, check.reg);
bool cannotReplace = inst.op == IROp::Interpret || inst.op == IROp::CallReplacement;
// If this doesn't directly read (i.e. Interpret), we can't swap.
bool cannotReplace = !IRReadsFromGPR(inst, check.reg, true);
if (!mutatesReg && !cannotReplace && check.srcReg >= 0 && lastWrittenTo[check.srcReg] < check.index) {
// Replace with the srcReg instead. This happens with non-nice delay slots.
// We're changing "Mov A, B; Add C, C, A" to "Mov A, B; Add C, C, B" here.
// srcReg should only be set when it was a Mov.
inst = IRReplaceSrcGPR(inst, check.reg, check.srcReg);
// If the Mov modified the same reg as this instruction, we can't optimize from it anymore.
if (inst.dest == check.reg) {
check.reg = 0;
// We can also optimize it out since we've essentially moved now.
insts[check.index].op = IROp::Mov;
insts[check.index].dest = 0;
insts[check.index].src1 = 0;
}
} else if (!IRMutatesDestGPR(insts[check.index], check.reg) && inst.op == IROp::Mov && i == check.index + 1) {
// As long as the previous inst wasn't modifying its dest reg, and this is a Mov, we can swap.
// We're changing "Add A, B, C; Mov B, A" to "Add B, B, C; Mov A, B" here.
// This happens with lwl/lwr temps. Replace the original dest.
insts[check.index] = IRReplaceDestGPR(insts[check.index], check.reg, inst.dest);
lastWrittenTo[inst.dest] = check.index;
// If it's being read from (by inst), we can't optimize out.
// If it's being read from (by inst now), we can't optimize out.
check.reg = 0;
// Update the read by exit flag to match the new reg.
check.readByExit = inst.dest < IRTEMP_0 || inst.dest > IRTEMP_LR_SHIFT;
@ -855,6 +884,7 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
check.reg = 0;
}
} else if (check.readByExit && (m->flags & IRFLAG_EXIT) != 0) {
// This is an exit, and the reg is read by any exit. Clear it.
check.reg = 0;
} else if (IRDestGPR(inst) == check.reg) {
// Clobbered, we can optimize out.
@ -878,7 +908,7 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
case IRTEMP_LR_VALUE:
case IRTEMP_LR_MASK:
case IRTEMP_LR_SHIFT:
// Unlike other ops, these don't need to persist between blocks.
// Unlike other registers, these don't need to persist between blocks.
// So we consider them not read unless proven read.
lastWrittenTo[dest] = i;
// If this is a copy, we might be able to optimize out the copy.
@ -911,6 +941,7 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
insts.push_back(inst);
}
// Since we're done with the instructions, all remaining can be nuked.
for (Check &check : checks) {
if (!check.readByExit && check.reg > 0) {
insts[check.index].op = IROp::Mov;
@ -920,6 +951,7 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
}
for (const IRInst &inst : insts) {
// Simply skip any Mov 0, 0 instructions, since that's how we nuke one.
if (inst.op != IROp::Mov || inst.dest != 0 || inst.src1 != 0) {
out.Write(inst);
}

View File

@ -280,9 +280,11 @@ namespace Reporting
return false;
if (http.Resolve(serverHost, ServerPort())) {
http.Connect();
int result = http.POST(http::RequestParams(uri), data, mimeType, output, &progress);
http.Disconnect();
int result = -1;
if (http.Connect()) {
result = http.POST(http::RequestParams(uri), data, mimeType, output, &progress);
http.Disconnect();
}
return result >= 200 && result < 300;
} else {
@ -388,6 +390,11 @@ namespace Reporting
everUnsupported = true;
}
void NotifyDebugger() {
currentSupported = false;
everUnsupported = true;
}
std::string CurrentGameID()
{
// TODO: Maybe ParamSFOData shouldn't include nulls in std::strings? Don't work to break savedata, though...

View File

@ -38,6 +38,9 @@ namespace Reporting
// Should be called whenever the game configuration changes.
void UpdateConfig();
// Should be called when debugging APIs are used in a way that could make the game crash.
void NotifyDebugger();
// Returns whether or not the reporting system is currently enabled.
bool IsEnabled();

View File

@ -62,6 +62,7 @@ void TextureReplacer::Init() {
void TextureReplacer::NotifyConfigChanged() {
gameID_ = g_paramSFO.GetDiscID();
bool wasEnabled = enabled_;
enabled_ = g_Config.bReplaceTextures || g_Config.bSaveNewTextures;
if (enabled_) {
basePath_ = GetSysDirectory(DIRECTORY_TEXTURES) / gameID_;
@ -75,6 +76,8 @@ void TextureReplacer::NotifyConfigChanged() {
}
enabled_ = File::Exists(basePath_) && File::IsDirectory(basePath_);
} else if (wasEnabled) {
Decimate(ReplacerDecimateMode::ALL);
}
if (enabled_) {
@ -407,7 +410,7 @@ void TextureReplacer::PopulateReplacement(ReplacedTexture *result, u64 cachekey,
}
ReplacedTextureLevel level;
level.fmt = ReplacedTextureFormat::F_8888;
level.fmt = Draw::DataFormat::R8G8B8A8_UNORM;
level.file = filename;
bool good = PopulateLevel(level);
@ -456,6 +459,11 @@ bool TextureReplacer::PopulateLevel(ReplacedTextureLevel &level) {
bool good = false;
FILE *fp = File::OpenCFile(level.file, "rb");
if (!fp) {
ERROR_LOG(G3D, "Error opening replacement texture file '%s'", level.file.c_str());
return false;
}
auto imageType = Identify(fp);
if (imageType == ReplacedImageType::ZIM) {
fseek(fp, 4, SEEK_SET);
@ -627,50 +635,13 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl
SimpleBuf<u32> saveBuf;
// Since we're copying, change the format meanwhile. Not much extra cost.
if (replacedInfo.fmt != ReplacedTextureFormat::F_8888) {
saveBuf.resize((pitch * h) / sizeof(u16));
switch (replacedInfo.fmt) {
case ReplacedTextureFormat::F_5650:
ConvertRGB565ToRGBA8888(saveBuf.data(), (const u16 *)data, (pitch * h) / sizeof(u16));
break;
case ReplacedTextureFormat::F_5551:
ConvertRGBA5551ToRGBA8888(saveBuf.data(), (const u16 *)data, (pitch * h) / sizeof(u16));
break;
case ReplacedTextureFormat::F_4444:
ConvertRGBA4444ToRGBA8888(saveBuf.data(), (const u16 *)data, (pitch * h) / sizeof(u16));
break;
case ReplacedTextureFormat::F_0565_ABGR:
ConvertBGR565ToRGBA8888(saveBuf.data(), (const u16 *)data, (pitch * h) / sizeof(u16));
break;
case ReplacedTextureFormat::F_1555_ABGR:
ConvertABGR1555ToRGBA8888(saveBuf.data(), (const u16 *)data, (pitch * h) / sizeof(u16));
break;
case ReplacedTextureFormat::F_4444_ABGR:
ConvertABGR4444ToRGBA8888(saveBuf.data(), (const u16 *)data, (pitch * h) / sizeof(u16));
break;
case ReplacedTextureFormat::F_8888_BGRA:
ConvertBGRA8888ToRGBA8888(saveBuf.data(), (const u32 *)data, (pitch * h) / sizeof(u32));
break;
case ReplacedTextureFormat::F_8888:
// Impossible. Just so we can get warnings on other missed formats.
break;
}
data = saveBuf.data();
if (replacedInfo.fmt != ReplacedTextureFormat::F_8888_BGRA) {
// We doubled our pitch.
pitch *= 2;
}
} else {
// Copy data to a buffer so we can send it to the thread. Might as well compact-away the pitch
// while we're at it.
saveBuf.resize(w * h);
for (int y = 0; y < h; y++) {
memcpy((u8 *)saveBuf.data() + y * w * 4, (const u8 *)data + y * pitch, w * sizeof(u32));
}
pitch = w * 4;
// Copy data to a buffer so we can send it to the thread. Might as well compact-away the pitch
// while we're at it.
saveBuf.resize(w * h);
for (int y = 0; y < h; y++) {
memcpy((u8 *)saveBuf.data() + y * w * 4, (const u8 *)data + y * pitch, w * sizeof(u32));
}
pitch = w * 4;
TextureSaveTask *task = new TextureSaveTask(std::move(saveBuf));
// Should probably do a proper move constructor but this'll work.
@ -686,16 +657,21 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl
// Remember that we've saved this for next time.
// Should be OK that the actual disk write may not be finished yet.
ReplacedTextureLevel saved;
saved.fmt = ReplacedTextureFormat::F_8888;
saved.fmt = Draw::DataFormat::R8G8B8A8_UNORM;
saved.file = filename;
saved.w = w;
saved.h = h;
savedCache_[replacementKey] = std::make_pair(saved, now);
}
void TextureReplacer::Decimate(bool forcePressure) {
void TextureReplacer::Decimate(ReplacerDecimateMode mode) {
// Allow replacements to be cached for a long time, although they're large.
const double age = forcePressure ? 90.0 : 1800.0;
double age = 1800.0;
if (mode == ReplacerDecimateMode::FORCE_PRESSURE)
age = 90.0;
else if (mode == ReplacerDecimateMode::ALL)
age = 0.0;
const double threshold = time_now_d() - age;
for (auto &item : cache_) {
item.second.PurgeIfOlder(threshold);
@ -846,6 +822,8 @@ bool ReplacedTexture::IsReady(double budget) {
return false;
if (g_Config.bReplaceTexturesAllowLate) {
if (threadWaitable_)
delete threadWaitable_;
threadWaitable_ = new LimitedWaitable();
g_threadManager.EnqueueTask(new ReplacedTextureTask(*this, threadWaitable_));
@ -867,8 +845,8 @@ void ReplacedTexture::Prepare() {
if (cancelPrepare_)
return;
levelData_.resize(MaxLevel() + 1);
for (int i = 0; i <= MaxLevel(); ++i) {
levelData_.resize(NumLevels());
for (int i = 0; i < NumLevels(); ++i) {
if (cancelPrepare_)
break;
PrepareData(i);

View File

@ -25,6 +25,7 @@
#include "Common/CommonTypes.h"
#include "Common/MemoryUtil.h"
#include "Common/File/Path.h"
#include "Common/GPU/DataFormat.h"
#include "GPU/Common/TextureDecoder.h"
#include "GPU/ge_constants.h"
@ -35,17 +36,6 @@ class TextureReplacer;
class ReplacedTextureTask;
class LimitedWaitable;
enum class ReplacedTextureFormat {
F_5650,
F_5551,
F_4444,
F_8888,
F_0565_ABGR,
F_1555_ABGR,
F_4444_ABGR,
F_8888_BGRA,
};
// These must match the constants in TextureCacheCommon.
enum class ReplacedTextureAlpha {
UNKNOWN = 0x04,
@ -62,7 +52,7 @@ enum class ReplacedTextureHash {
struct ReplacedTextureLevel {
int w;
int h;
ReplacedTextureFormat fmt;
Draw::DataFormat fmt; // NOTE: Right now, the only supported format is Draw::DataFormat::R8G8B8A8_UNORM.
Path file;
};
@ -70,8 +60,7 @@ struct ReplacementCacheKey {
u64 cachekey;
u32 hash;
ReplacementCacheKey(u64 c, u32 h) : cachekey(c), hash(h) {
}
ReplacementCacheKey(u64 c, u32 h) : cachekey(c), hash(h) { }
bool operator ==(const ReplacementCacheKey &k) const {
return k.cachekey == cachekey && k.hash == hash;
@ -95,8 +84,7 @@ struct ReplacementAliasKey {
};
};
ReplacementAliasKey(u64 c, u32 h, u32 l) : cachekey(c), level(l), hash(h) {
}
ReplacementAliasKey(u64 c, u32 h, u32 l) : cachekey(c), level(l), hash(h) { }
bool operator ==(const ReplacementAliasKey &k) const {
return k.cachekey == cachekey && k.hashAndLevel == hashAndLevel;
@ -129,11 +117,11 @@ namespace std {
struct ReplacedTexture {
~ReplacedTexture();
inline bool Valid() {
inline bool Valid() const {
return !levels_.empty();
}
bool GetSize(int level, int &w, int &h) {
bool GetSize(int level, int &w, int &h) const {
if ((size_t)level < levels_.size()) {
w = levels_[level].w;
h = levels_[level].h;
@ -142,18 +130,18 @@ struct ReplacedTexture {
return false;
}
int MaxLevel() {
return (int)levels_.size() - 1;
int NumLevels() const {
return (int)levels_.size();
}
ReplacedTextureFormat Format(int level) {
Draw::DataFormat Format(int level) const {
if ((size_t)level < levels_.size()) {
return levels_[level].fmt;
}
return ReplacedTextureFormat::F_8888;
return Draw::DataFormat::R8G8B8A8_UNORM;
}
u8 AlphaStatus() {
u8 AlphaStatus() const {
return (u8)alphaStatus_;
}
@ -185,7 +173,13 @@ struct ReplacedTextureDecodeInfo {
bool isVideo;
bool isFinal;
int scaleFactor;
ReplacedTextureFormat fmt;
Draw::DataFormat fmt;
};
enum class ReplacerDecimateMode {
NEW_FRAME,
FORCE_PRESSURE,
ALL,
};
class TextureReplacer {
@ -214,7 +208,7 @@ public:
// Notify that a new texture was decoded. May already be upscaled, saves the data passed.
void NotifyTextureDecoded(const ReplacedTextureDecodeInfo &replacedInfo, const void *data, int pitch, int level, int w, int h);
void Decimate(bool forcePressure);
void Decimate(ReplacerDecimateMode mode);
static bool GenerateIni(const std::string &gameID, Path &generatedFilename);
static bool IniExists(const std::string &gameID);

View File

@ -69,6 +69,12 @@ FramebufferManagerCommon::~FramebufferManagerCommon() {
}
bvfbs_.clear();
// Shouldn't be anything left here in theory, but just in case...
for (auto trackedDepth : trackedDepthBuffers_) {
delete trackedDepth;
}
trackedDepthBuffers_.clear();
delete presentation_;
}
@ -400,9 +406,16 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
ResizeFramebufFBO(vfb, drawing_width, drawing_height, true);
NotifyRenderFramebufferCreated(vfb);
// Looks up by z_address, so if one is found here and not have last pointers equal to this one,
// there is another one.
TrackedDepthBuffer *prevDepth = GetOrCreateTrackedDepthBuffer(vfb);
// We might already want to copy depth, in case this is a temp buffer. See #7810.
if (currentRenderVfb_ && !params.isClearingDepth) {
BlitFramebufferDepth(currentRenderVfb_, vfb);
if (prevDepth->vfb != vfb) {
if (!params.isClearingDepth && prevDepth->vfb) {
BlitFramebufferDepth(prevDepth->vfb, vfb);
}
prevDepth->vfb = vfb;
}
SetColorUpdated(vfb, skipDrawReason);
@ -495,6 +508,13 @@ void FramebufferManagerCommon::DestroyFramebuf(VirtualFramebuffer *v) {
if (prevPrevDisplayFramebuf_ == v)
prevPrevDisplayFramebuf_ = nullptr;
// Remove any depth buffer tracking related to this vfb.
for (auto it = trackedDepthBuffers_.begin(); it != trackedDepthBuffers_.end(); it++) {
if ((*it)->vfb == v) {
(*it)->vfb = nullptr; // Mark for deletion in the next Decimate
}
}
delete v;
}
@ -503,9 +523,10 @@ void FramebufferManagerCommon::BlitFramebufferDepth(VirtualFramebuffer *src, Vir
// Check that the depth address is even the same before actually blitting.
bool matchingDepthBuffer = src->z_address == dst->z_address && src->z_stride != 0 && dst->z_stride != 0;
bool matchingSize = src->width == dst->width && src->height == dst->height;
if (!matchingDepthBuffer || !matchingSize)
bool matchingSize = (src->width == dst->width || src->width == 512 && dst->width == 480) || (src->width == 480 && dst->width == 512) && src->height == dst->height;
if (!matchingDepthBuffer || !matchingSize) {
return;
}
// Copy depth value from the previously bound framebuffer to the current one.
bool hasNewerDepth = src->last_frame_depth_render != 0 && src->last_frame_depth_render >= dst->last_frame_depth_updated;
@ -520,7 +541,9 @@ void FramebufferManagerCommon::BlitFramebufferDepth(VirtualFramebuffer *src, Vir
int w = std::min(src->renderWidth, dst->renderWidth);
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.
// 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.
// TODO: This ordering should probably apply to GL only, since in Vulkan you can totally copy just the depth aspect.
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("After BlitFramebufferDepth");
@ -531,6 +554,34 @@ void FramebufferManagerCommon::BlitFramebufferDepth(VirtualFramebuffer *src, Vir
dst->last_frame_depth_updated = gpuStats.numFlips;
}
TrackedDepthBuffer *FramebufferManagerCommon::GetOrCreateTrackedDepthBuffer(VirtualFramebuffer *vfb) {
for (auto tracked : trackedDepthBuffers_) {
// Disable tracking if color of the new vfb is clashing with tracked depth.
if (vfb->fb_address == tracked->z_address) {
tracked->vfb = nullptr; // this is checked for. Cheaper than deleting.
continue;
}
if (vfb->z_address == tracked->z_address) {
if (vfb->z_stride == tracked->z_stride) {
return tracked;
} else {
// Stride has changed, mark as bad.
tracked->vfb = nullptr;
}
}
}
TrackedDepthBuffer *tracked = new TrackedDepthBuffer();
tracked->vfb = vfb;
tracked->z_address = vfb->z_address;
tracked->z_stride = vfb->z_stride;
trackedDepthBuffers_.push_back(tracked);
return tracked;
}
void FramebufferManagerCommon::NotifyRenderFramebufferCreated(VirtualFramebuffer *vfb) {
if (!useBufferedRendering_) {
// Let's ignore rendering to targets that have not (yet) been displayed.
@ -738,8 +789,14 @@ void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffe
shaderManager_->DirtyLastShader();
// Copy depth between the framebuffers, if the z_address is the same (checked inside.)
if (prevVfb && !isClearingDepth) {
BlitFramebufferDepth(prevVfb, vfb);
TrackedDepthBuffer *prevDepth = GetOrCreateTrackedDepthBuffer(vfb);
// We might already want to copy depth, in case this is a temp buffer. See #7810.
if (prevDepth->vfb != vfb) {
if (!isClearingDepth && prevDepth->vfb) {
BlitFramebufferDepth(prevDepth->vfb, vfb);
}
prevDepth->vfb = vfb;
}
if (vfb->drawnFormat != vfb->format) {
@ -1266,6 +1323,16 @@ void FramebufferManagerCommon::DecimateFBOs() {
bvfbs_.erase(bvfbs_.begin() + i--);
}
}
// Also clean up the TrackedDepthBuffer array...
for (auto it = trackedDepthBuffers_.begin(); it != trackedDepthBuffers_.end(); it) {
if ((*it)->vfb == nullptr) {
delete *it;
it = trackedDepthBuffers_.erase(it);
} else {
it++;
}
}
}
// Requires width/height to be set already.

View File

@ -57,6 +57,11 @@ namespace Draw {
class VulkanFBO;
// We have to track VFBs and depth buffers together, since bits are shared between the color alpha channel
// and the stencil buffer on the PSP.
// Sometimes, virtual framebuffers need to share a Z buffer. We emulate this by copying from on to the next
// when such a situation is detected. In order to reliably detect this, we separately track depth buffers,
// and they know which color buffer they were used with last.
struct VirtualFramebuffer {
u32 fb_address;
u32 z_address; // If 0, it's a "RAM" framebuffer.
@ -113,6 +118,18 @@ struct VirtualFramebuffer {
bool firstFrameSaved;
};
struct TrackedDepthBuffer {
u32 z_address;
int z_stride;
// Really need to make sure we're killing these TrackedDepthBuffer's off when the VirtualFrameBuffers die.
VirtualFramebuffer *vfb;
// Could do full tracking of which framebuffers are used with this depth buffer,
// but probably not necessary.
// std::set<std::pair<u32, u32>> seen_fbs;
};
struct FramebufferHeuristicParams {
u32 fb_address;
int fb_stride;
@ -249,6 +266,8 @@ public:
void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes);
void DrawFramebufferToOutput(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride);
TrackedDepthBuffer *GetOrCreateTrackedDepthBuffer(VirtualFramebuffer *vfb);
void DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height);
size_t NumVFBs() const { return vfbs_.size(); }
@ -411,6 +430,8 @@ protected:
std::vector<VirtualFramebuffer *> vfbs_;
std::vector<VirtualFramebuffer *> bvfbs_; // blitting framebuffers (for download)
std::vector<TrackedDepthBuffer *> trackedDepthBuffers_;
bool gameUsesSequentialCopies_ = false;
// Sampled in BeginFrame/UpdateSize for safety.

View File

@ -1089,9 +1089,12 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead,
case REPLACE_BLEND_BLUE_TO_ALPHA:
blueToAlpha = true;
blendState.enabled = gstate.isAlphaBlendEnabled();
// We'll later convert the color blend to blend in the alpha channel.
break;
case REPLACE_BLEND_COPY_FBO:
blendState.enabled = true;
blendState.applyFramebufferRead = true;
blendState.resetFramebufferRead = false;
blendState.replaceAlphaWithStencil = replaceAlphaWithStencil;
@ -1099,16 +1102,17 @@ void ConvertBlendState(GenericBlendState &blendState, bool allowFramebufferRead,
case REPLACE_BLEND_PRE_SRC:
case REPLACE_BLEND_PRE_SRC_2X_ALPHA:
blendState.enabled = true;
usePreSrc = true;
break;
case REPLACE_BLEND_STANDARD:
case REPLACE_BLEND_2X_ALPHA:
case REPLACE_BLEND_2X_SRC:
blendState.enabled = true;
break;
}
blendState.enabled = true;
blendState.resetFramebufferRead = true;
const GEBlendMode blendFuncEq = gstate.getBlendEq();

View File

@ -25,6 +25,7 @@
#include "Common/MemoryUtil.h"
#include "Common/StringUtils.h"
#include "Common/TimeUtil.h"
#include "Common/Math/math_util.h"
#include "Core/Config.h"
#include "Core/Debugger/MemBlockInfo.h"
#include "Core/Reporting.h"
@ -743,7 +744,7 @@ void TextureCacheCommon::Decimate(bool forcePressure) {
}
DecimateVideos();
replacer_.Decimate(forcePressure);
replacer_.Decimate(forcePressure ? ReplacerDecimateMode::FORCE_PRESSURE : ReplacerDecimateMode::NEW_FRAME);
}
void TextureCacheCommon::DecimateVideos() {
@ -756,9 +757,9 @@ void TextureCacheCommon::DecimateVideos() {
}
}
bool TextureCacheCommon::IsVideo(u32 texaddr) {
bool TextureCacheCommon::IsVideo(u32 texaddr) const {
texaddr &= 0x3FFFFFFF;
for (auto info : videos_) {
for (auto &info : videos_) {
if (texaddr < info.addr) {
continue;
}
@ -1319,7 +1320,7 @@ ReplacedTexture &TextureCacheCommon::FindReplacement(TexCacheEntry *entry, int &
// This is only used in the GLES backend, where we don't point these to video memory.
// So we shouldn't add a check for dstBuf != srcBuf, as long as the functions we call can handle that.
static void ReverseColors(void *dstBuf, const void *srcBuf, GETextureFormat fmt, int numPixels, bool useBGRA) {
static void ReverseColors(void *dstBuf, const void *srcBuf, GETextureFormat fmt, int numPixels) {
switch (fmt) {
case GE_TFMT_4444:
ConvertRGBA4444ToABGR4444((u16 *)dstBuf, (const u16 *)srcBuf, numPixels);
@ -1332,12 +1333,9 @@ static void ReverseColors(void *dstBuf, const void *srcBuf, GETextureFormat fmt,
ConvertRGB565ToBGR565((u16 *)dstBuf, (const u16 *)srcBuf, numPixels);
break;
default:
if (useBGRA) {
ConvertRGBA8888ToBGRA8888((u32 *)dstBuf, (const u32 *)srcBuf, numPixels);
} else {
// No need to convert RGBA8888, right order already
if (dstBuf != srcBuf)
memcpy(dstBuf, srcBuf, numPixels * sizeof(u32));
// No need to convert RGBA8888, right order already
if (dstBuf != srcBuf) {
memcpy(dstBuf, srcBuf, numPixels * sizeof(u32));
}
break;
}
@ -1367,7 +1365,7 @@ static inline void ConvertFormatToRGBA8888(GEPaletteFormat format, u32 *dst, con
template <typename DXTBlock, int n>
static CheckAlphaResult DecodeDXTBlocks(uint8_t *out, int outPitch, uint32_t texaddr, const uint8_t *texptr,
int w, int h, int bufw, bool reverseColors, bool useBGRA) {
int w, int h, int bufw, bool reverseColors) {
int minw = std::min(bufw, w);
uint32_t *dst = (uint32_t *)out;
@ -1403,7 +1401,7 @@ static CheckAlphaResult DecodeDXTBlocks(uint8_t *out, int outPitch, uint32_t tex
}
if (reverseColors) {
ReverseColors(out, out, GE_TFMT_8888, outPitch32 * h, useBGRA);
ReverseColors(out, out, GE_TFMT_8888, outPitch32 * h);
}
if (n == 1) {
@ -1434,7 +1432,7 @@ inline u32 TfmtRawToFullAlpha(GETextureFormat fmt) {
}
}
CheckAlphaResult TextureCacheCommon::DecodeTextureLevel(u8 *out, int outPitch, GETextureFormat format, GEPaletteFormat clutformat, uint32_t texaddr, int level, int bufw, bool reverseColors, bool useBGRA, bool expandTo32bit) {
CheckAlphaResult TextureCacheCommon::DecodeTextureLevel(u8 *out, int outPitch, GETextureFormat format, GEPaletteFormat clutformat, uint32_t texaddr, int level, int bufw, bool reverseColors, bool expandTo32bit) {
u32 alphaSum = 0xFFFFFFFF;
u32 fullAlphaMask = 0x0;
@ -1545,17 +1543,18 @@ CheckAlphaResult TextureCacheCommon::DecodeTextureLevel(u8 *out, int outPitch, G
if (!swizzled) {
// Just a simple copy, we swizzle the color format.
fullAlphaMask = TfmtRawToFullAlpha(format);
if (reverseColors) {
// Just check the input's alpha to reuse code. TODO: make a specialized ReverseColors that checks as we go.
for (int y = 0; y < h; ++y) {
CheckMask16((const u16 *)(texptr + bufw * sizeof(u16) * y), w, &alphaSum);
ReverseColors(out + outPitch * y, texptr + bufw * sizeof(u16) * y, format, w, useBGRA);
}
} else if (expandTo32bit) {
if (expandTo32bit) {
// This is OK even if reverseColors is on, because it expands to the 8888 format which is the same in reverse mode.
for (int y = 0; y < h; ++y) {
CheckMask16((const u16 *)(texptr + bufw * sizeof(u16) * y), w, &alphaSum);
ConvertFormatToRGBA8888(format, (u32 *)(out + outPitch * y), (const u16 *)texptr + bufw * y, w);
}
} else if (reverseColors) {
// Just check the input's alpha to reuse code. TODO: make a specialized ReverseColors that checks as we go.
for (int y = 0; y < h; ++y) {
CheckMask16((const u16 *)(texptr + bufw * sizeof(u16) * y), w, &alphaSum);
ReverseColors(out + outPitch * y, texptr + bufw * sizeof(u16) * y, format, w);
}
} else {
for (int y = 0; y < h; ++y) {
CopyAndSumMask16((u16 *)(out + outPitch * y), (u16 *)(texptr + bufw * sizeof(u16) * y), w, &alphaSum);
@ -1575,18 +1574,19 @@ CheckAlphaResult TextureCacheCommon::DecodeTextureLevel(u8 *out, int outPitch, G
const u8 *unswizzled = (u8 *)tmpTexBuf32_.data();
fullAlphaMask = TfmtRawToFullAlpha(format);
if (reverseColors) {
// Just check the swizzled input's alpha to reuse code. TODO: make a specialized ReverseColors that checks as we go.
for (int y = 0; y < h; ++y) {
CheckMask16((const u16 *)(unswizzled + bufw * sizeof(u16) * y), w, &alphaSum);
ReverseColors(out + outPitch * y, unswizzled + bufw * sizeof(u16) * y, format, w, useBGRA);
}
} else if (expandTo32bit) {
if (expandTo32bit) {
// This is OK even if reverseColors is on, because it expands to the 8888 format which is the same in reverse mode.
// Just check the swizzled input's alpha to reuse code. TODO: make a specialized ConvertFormatToRGBA8888 that checks as we go.
for (int y = 0; y < h; ++y) {
CheckMask16((const u16 *)(unswizzled + bufw * sizeof(u16) * y), w, &alphaSum);
ConvertFormatToRGBA8888(format, (u32 *)(out + outPitch * y), (const u16 *)unswizzled + bufw * y, w);
}
} else if (reverseColors) {
// Just check the swizzled input's alpha to reuse code. TODO: make a specialized ReverseColors that checks as we go.
for (int y = 0; y < h; ++y) {
CheckMask16((const u16 *)(unswizzled + bufw * sizeof(u16) * y), w, &alphaSum);
ReverseColors(out + outPitch * y, unswizzled + bufw * sizeof(u16) * y, format, w);
}
} else {
for (int y = 0; y < h; ++y) {
CopyAndSumMask16((u16 *)(out + outPitch * y), (const u16 *)(unswizzled + bufw * sizeof(u16) * y), w, &alphaSum);
@ -1604,7 +1604,7 @@ CheckAlphaResult TextureCacheCommon::DecodeTextureLevel(u8 *out, int outPitch, G
if (reverseColors) {
for (int y = 0; y < h; ++y) {
CheckMask32((const u32 *)(texptr + bufw * sizeof(u32) * y), w, &alphaSum);
ReverseColors(out + outPitch * y, texptr + bufw * sizeof(u32) * y, format, w, useBGRA);
ReverseColors(out + outPitch * y, texptr + bufw * sizeof(u32) * y, format, w);
}
} else {
for (int y = 0; y < h; ++y) {
@ -1626,7 +1626,7 @@ CheckAlphaResult TextureCacheCommon::DecodeTextureLevel(u8 *out, int outPitch, G
if (reverseColors) {
for (int y = 0; y < h; ++y) {
CheckMask32((const u32 *)(unswizzled + bufw * sizeof(u32) * y), w, &alphaSum);
ReverseColors(out + outPitch * y, unswizzled + bufw * sizeof(u32) * y, format, w, useBGRA);
ReverseColors(out + outPitch * y, unswizzled + bufw * sizeof(u32) * y, format, w);
}
} else {
for (int y = 0; y < h; ++y) {
@ -1637,13 +1637,13 @@ CheckAlphaResult TextureCacheCommon::DecodeTextureLevel(u8 *out, int outPitch, G
break;
case GE_TFMT_DXT1:
return DecodeDXTBlocks<DXT1Block, 1>(out, outPitch, texaddr, texptr, w, h, bufw, reverseColors, useBGRA);
return DecodeDXTBlocks<DXT1Block, 1>(out, outPitch, texaddr, texptr, w, h, bufw, reverseColors);
case GE_TFMT_DXT3:
return DecodeDXTBlocks<DXT3Block, 3>(out, outPitch, texaddr, texptr, w, h, bufw, reverseColors, useBGRA);
return DecodeDXTBlocks<DXT3Block, 3>(out, outPitch, texaddr, texptr, w, h, bufw, reverseColors);
case GE_TFMT_DXT5:
return DecodeDXTBlocks<DXT5Block, 5>(out, outPitch, texaddr, texptr, w, h, bufw, reverseColors, useBGRA);
return DecodeDXTBlocks<DXT5Block, 5>(out, outPitch, texaddr, texptr, w, h, bufw, reverseColors);
default:
ERROR_LOG_REPORT(G3D, "Unknown Texture Format %d!!!", format);
@ -2009,3 +2009,202 @@ void TextureCacheCommon::ClearNextFrame() {
std::string AttachCandidate::ToString() {
return StringFromFormat("[C:%08x/%d Z:%08x/%d X:%d Y:%d reint: %s]", this->fb->fb_address, this->fb->fb_stride, this->fb->z_address, this->fb->z_stride, this->match.xOffset, this->match.yOffset, this->match.reinterpret ? "true" : "false");
}
bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEntry *entry) {
gpuStats.numTexturesDecoded++;
// For the estimate, we assume cluts always point to 8888 for simplicity.
cacheSizeEstimate_ += EstimateTexMemoryUsage(entry);
if ((entry->bufw == 0 || (gstate.texbufwidth[0] & 0xf800) != 0) && entry->addr >= PSP_GetKernelMemoryEnd()) {
ERROR_LOG_REPORT(G3D, "Texture with unexpected bufw (full=%d)", gstate.texbufwidth[0] & 0xffff);
// Proceeding here can cause a crash.
return false;
}
plan.badMipSizes = false;
// maxLevel here is the max level to upload. Not the count.
plan.levelsToLoad = entry->maxLevel + 1;
for (int i = 0; i < plan.levelsToLoad; i++) {
// If encountering levels pointing to nothing, adjust max level.
u32 levelTexaddr = gstate.getTextureAddress(i);
if (!Memory::IsValidAddress(levelTexaddr)) {
plan.levelsToLoad = i;
break;
}
// If size reaches 1, stop, and override maxlevel.
int tw = gstate.getTextureWidth(i);
int th = gstate.getTextureHeight(i);
if (tw == 1 || th == 1) {
plan.levelsToLoad = i + 1; // next level is assumed to be invalid
break;
}
if (i > 0) {
int lastW = gstate.getTextureWidth(i - 1);
int lastH = gstate.getTextureHeight(i - 1);
if (gstate_c.Supports(GPU_SUPPORTS_TEXTURE_LOD_CONTROL)) {
if (tw != 1 && tw != (lastW >> 1))
plan.badMipSizes = true;
else if (th != 1 && th != (lastH >> 1))
plan.badMipSizes = true;
}
}
}
plan.scaleFactor = standardScaleFactor_;
// Rachet down scale factor in low-memory mode.
// TODO: I think really we should just turn it off?
if (lowMemoryMode_ && !plan.hardwareScaling) {
// Keep it even, though, just in case of npot troubles.
plan.scaleFactor = plan.scaleFactor > 4 ? 4 : (plan.scaleFactor > 2 ? 2 : 1);
}
if (plan.badMipSizes) {
plan.levelsToLoad = 1;
}
if (plan.hardwareScaling) {
plan.scaleFactor = shaderScaleFactor_;
}
// We generate missing mipmaps from maxLevel+1 up to this level. maxLevel can get overwritten below
// such as when using replacement textures - but let's keep the same amount of levels for generation.
// Not all backends will generate mipmaps, and in GL we can't really control the number of levels.
plan.levelsToCreate = plan.levelsToLoad;
plan.w = gstate.getTextureWidth(0);
plan.h = gstate.getTextureHeight(0);
plan.replaced = &FindReplacement(entry, plan.w, plan.h);
if (plan.replaced->Valid()) {
// We're replacing, so we won't scale.
plan.scaleFactor = 1;
plan.levelsToLoad = plan.replaced->NumLevels();
plan.badMipSizes = false;
}
// Don't scale the PPGe texture.
if (entry->addr > 0x05000000 && entry->addr < PSP_GetKernelMemoryEnd())
plan.scaleFactor = 1;
if ((entry->status & TexCacheEntry::STATUS_CHANGE_FREQUENT) != 0 && plan.scaleFactor != 1 && plan.slowScaler) {
// Remember for later that we /wanted/ to scale this texture.
entry->status |= TexCacheEntry::STATUS_TO_SCALE;
plan.scaleFactor = 1;
}
if (plan.scaleFactor != 1) {
if (texelsScaledThisFrame_ >= TEXCACHE_MAX_TEXELS_SCALED && plan.slowScaler) {
entry->status |= TexCacheEntry::STATUS_TO_SCALE;
plan.scaleFactor = 1;
} else {
entry->status &= ~TexCacheEntry::STATUS_TO_SCALE;
entry->status |= TexCacheEntry::STATUS_IS_SCALED;
texelsScaledThisFrame_ += plan.w * plan.h;
}
}
plan.isVideo = IsVideo(entry->addr);
// TODO: Support reading actual mip levels for upscaled images, instead of just generating them.
// Maybe can just remove this check?
if (plan.scaleFactor > 1) {
plan.levelsToLoad = 1;
bool enableVideoUpscaling = false;
if (!enableVideoUpscaling && plan.isVideo) {
plan.scaleFactor = 1;
plan.levelsToCreate = 1;
}
}
// Always load base level texture here
plan.baseLevelSrc = 0;
if (IsFakeMipmapChange()) {
// NOTE: Since the level is not part of the cache key, we assume it never changes.
plan.baseLevelSrc = std::max(0, gstate.getTexLevelOffset16() / 16);
plan.levelsToCreate = 1;
plan.levelsToLoad = 1;
}
if (plan.levelsToCreate == 1) {
entry->status |= TexCacheEntry::STATUS_NO_MIPS;
} else {
entry->status &= ~TexCacheEntry::STATUS_NO_MIPS;
}
// Will be filled in again during decode.
entry->status &= ~TexCacheEntry::STATUS_ALPHA_MASK;
return true;
}
void TextureCacheCommon::LoadTextureLevel(TexCacheEntry &entry, uint8_t *data, int stride, ReplacedTexture &replaced, int srcLevel, int scaleFactor, Draw::DataFormat dstFmt, bool reverseColors) {
int w = gstate.getTextureWidth(srcLevel);
int h = gstate.getTextureHeight(srcLevel);
PROFILE_THIS_SCOPE("decodetex");
if (replaced.GetSize(srcLevel, w, h)) {
double replaceStart = time_now_d();
replaced.Load(srcLevel, data, stride);
replacementTimeThisFrame_ += time_now_d() - replaceStart;
} else {
GETextureFormat tfmt = (GETextureFormat)entry.format;
GEPaletteFormat clutformat = gstate.getClutPaletteFormat();
u32 texaddr = gstate.getTextureAddress(srcLevel);
int bufw = GetTextureBufw(srcLevel, texaddr, tfmt);
u32 *pixelData;
int decPitch;
if (scaleFactor > 1) {
tmpTexBufRearrange_.resize(std::max(bufw, w) * h);
pixelData = tmpTexBufRearrange_.data();
// We want to end up with a neatly packed texture for scaling.
decPitch = w * 4;
} else {
pixelData = (u32 *)data;
decPitch = stride;
}
bool expand32 = !gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS) || scaleFactor > 1;
CheckAlphaResult alphaResult = DecodeTextureLevel((u8 *)pixelData, decPitch, tfmt, clutformat, texaddr, srcLevel, bufw, reverseColors, expand32);
entry.SetAlphaStatus(alphaResult, srcLevel);
if (scaleFactor > 1) {
// Note that this updates w and h!
scaler_.ScaleAlways((u32 *)data, pixelData, w, h, scaleFactor);
pixelData = (u32 *)data;
decPitch = w * 4;
if (decPitch != stride) {
// Rearrange in place to match the requested pitch.
// (it can only be larger than w * bpp, and a match is likely.)
// Note! This is bad because it reads the mapped memory! TODO: Look into if DX9 does this right.
for (int y = h - 1; y >= 0; --y) {
memcpy((u8 *)data + stride * y, (u8 *)data + decPitch * y, w * 4);
}
decPitch = stride;
}
}
if (replacer_.Enabled()) {
ReplacedTextureDecodeInfo replacedInfo;
replacedInfo.cachekey = entry.CacheKey();
replacedInfo.hash = entry.fullhash;
replacedInfo.addr = entry.addr;
replacedInfo.isVideo = IsVideo(entry.addr);
replacedInfo.isFinal = (entry.status & TexCacheEntry::STATUS_TO_SCALE) == 0;
replacedInfo.scaleFactor = scaleFactor;
replacedInfo.fmt = dstFmt;
// NOTE: Reading the decoded texture here may be very slow, if we just wrote it to write-combined memory.
replacer_.NotifyTextureDecoded(replacedInfo, pixelData, decPitch, srcLevel, w, h);
}
}
}

View File

@ -27,6 +27,7 @@
#include "Core/System.h"
#include "GPU/Common/GPUDebugInterface.h"
#include "GPU/Common/TextureDecoder.h"
#include "GPU/Common/TextureScalerCommon.h"
enum FramebufferNotification {
NOTIFY_FB_CREATED,
@ -129,7 +130,7 @@ struct TexCacheEntry {
// texture, and set this flag to allow scaling the texture just once for the new hash.
STATUS_FREE_CHANGE = 0x0400, // Allow one change before marking "frequent".
STATUS_BAD_MIPS = 0x0800, // Has bad or unusable mipmap levels.
STATUS_NO_MIPS = 0x0800, // Has bad or unusable mipmap levels.
STATUS_FRAMEBUFFER_OVERLAP = 0x1000,
@ -229,6 +230,44 @@ struct AttachCandidate {
class FramebufferManagerCommon;
struct BuildTexturePlan {
// Inputs
bool hardwareScaling = false;
bool slowScaler = true;
// Set if the PSP software specified an unusual mip chain,
// such as the same size throughout, or anything else that doesn't divide by
// two on each level. If this is set, we won't generate mips nor use any.
// However, we still respect baseLevelSrc.
bool badMipSizes;
// Number of mip levels to load from PSP memory (or replacement).
int levelsToLoad;
// The number of levels in total to create.
// If greater than maxLevelToLoad, the backend is expected to either generate
// the missing levels, or limit itself to levelsToLoad levels.
int levelsToCreate;
// Load the 0-mip from this PSP texture level instead of 0.
// If non-zero, we are only loading one level.
int baseLevelSrc;
// The scale factor of the final texture.
int scaleFactor;
// Whether it's a video texture or not. Some decisions might depend on this.
bool isVideo;
// Unscaled size of the 0-mip of the original texture.
// Don't really need to have it here, but convenient.
int w;
int h;
// The replacement for the texture.
ReplacedTexture *replaced;
};
class TextureCacheCommon {
public:
TextureCacheCommon(Draw::DrawContext *draw);
@ -270,6 +309,8 @@ public:
virtual bool GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) { return false; }
protected:
bool PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEntry *entry);
virtual void BindTexture(TexCacheEntry *entry) = 0;
virtual void Unbind() = 0;
virtual void ReleaseTexture(TexCacheEntry *entry, bool delete_them) = 0;
@ -283,11 +324,14 @@ protected:
virtual void UpdateCurrentClut(GEPaletteFormat clutFormat, u32 clutBase, bool clutIndexIsSimple) = 0;
bool CheckFullHash(TexCacheEntry *entry, bool &doDelete);
CheckAlphaResult DecodeTextureLevel(u8 *out, int outPitch, GETextureFormat format, GEPaletteFormat clutformat, uint32_t texaddr, int level, int bufw, bool reverseColors, bool useBGRA, bool expandTo32Bit);
CheckAlphaResult DecodeTextureLevel(u8 *out, int outPitch, GETextureFormat format, GEPaletteFormat clutformat, uint32_t texaddr, int level, int bufw, bool reverseColors, bool expandTo32Bit);
void UnswizzleFromMem(u32 *dest, u32 destPitch, const u8 *texptr, u32 bufw, u32 height, u32 bytesPerPixel);
CheckAlphaResult ReadIndexedTex(u8 *out, int outPitch, int level, const u8 *texptr, int bytesPerIndex, int bufw, bool reverseColors, bool expandTo32Bit);
ReplacedTexture &FindReplacement(TexCacheEntry *entry, int &w, int &h);
// Return value is mapData normally, but could be another buffer allocated with AllocateAlignedMemory.
void LoadTextureLevel(TexCacheEntry &entry, uint8_t *mapData, int mapRowPitch, ReplacedTexture &replaced, int srcLevel, int scaleFactor, Draw::DataFormat dstFmt, bool reverseColors);
template <typename T>
inline const T *GetCurrentClut() {
return (const T *)clutBuf_;
@ -307,7 +351,7 @@ protected:
void SetTextureFramebuffer(const AttachCandidate &candidate);
void DecimateVideos();
bool IsVideo(u32 texaddr);
bool IsVideo(u32 texaddr) const;
inline u32 QuickTexHash(TextureReplacer &replacer, u32 addr, int bufw, int w, int h, GETextureFormat format, TexCacheEntry *entry) const {
if (replacer.Enabled()) {
@ -336,6 +380,7 @@ protected:
Draw::DrawContext *draw_;
TextureReplacer replacer_;
TextureScalerCommon scaler_;
FramebufferManagerCommon *framebufferManager_;
bool clearCacheNextFrame_ = false;
@ -384,6 +429,7 @@ protected:
u16 clutAlphaLinearColor_;
int standardScaleFactor_;
int shaderScaleFactor_ = 0;
const char *nextChangeReason_;
bool nextNeedsRehash_;

View File

@ -499,30 +499,21 @@ TextureScalerCommon::TextureScalerCommon() {
TextureScalerCommon::~TextureScalerCommon() {
}
bool TextureScalerCommon::IsEmptyOrFlat(u32* data, int pixels, int fmt) {
int pixelsPerWord = 4 / BytesPerPixel(fmt);
bool TextureScalerCommon::IsEmptyOrFlat(const u32 *data, int pixels) const {
u32 ref = data[0];
if (pixelsPerWord > 1 && (ref & 0x0000FFFF) != (ref >> 16)) {
return false;
}
for (int i = 0; i < pixels / pixelsPerWord; ++i) {
if (data[i] != ref) return false;
// TODO: SIMD-ify this (although, for most textures we'll get out very early)
for (int i = 1; i < pixels; ++i) {
if (data[i] != ref)
return false;
}
return true;
}
void TextureScalerCommon::ScaleAlways(u32 *out, u32 *src, u32 &dstFmt, int &width, int &height, int factor) {
if (IsEmptyOrFlat(src, width*height, dstFmt)) {
void TextureScalerCommon::ScaleAlways(u32 *out, u32 *src, int &width, int &height, int factor) {
if (IsEmptyOrFlat(src, width * height)) {
// This means it was a flat texture. Vulkan wants the size up front, so we need to make it happen.
u32 pixel;
// Since it's flat, one pixel is enough. It might end up pointing to data, though.
u32 *pixelPointer = &pixel;
ConvertTo8888(dstFmt, src, pixelPointer, 1, 1);
if (pixelPointer != &pixel) {
pixel = *pixelPointer;
}
u32 pixel = *src;
dstFmt = Get8888Format();
width *= factor;
height *= factor;
@ -536,24 +527,20 @@ void TextureScalerCommon::ScaleAlways(u32 *out, u32 *src, u32 &dstFmt, int &widt
}
}
} else {
ScaleInto(out, src, dstFmt, width, height, factor);
ScaleInto(out, src, width, height, factor);
}
}
bool TextureScalerCommon::ScaleInto(u32 *outputBuf, u32 *src, u32 &dstFmt, int &width, int &height, int factor) {
bool TextureScalerCommon::ScaleInto(u32 *outputBuf, u32 *src, int &width, int &height, int factor) {
#ifdef SCALING_MEASURE_TIME
double t_start = time_now_d();
#endif
bufInput.resize(width*height); // used to store the input image image if it needs to be reformatted
u32 *inputBuf = bufInput.data();
// convert texture to correct format for scaling
ConvertTo8888(dstFmt, src, inputBuf, width, height);
u32 *inputBuf = src;
// deposterize
if (g_Config.bTexDeposterize) {
bufDeposter.resize(width*height);
bufDeposter.resize(width * height);
DePosterize(inputBuf, bufDeposter.data(), width, height);
inputBuf = bufDeposter.data();
}
@ -577,7 +564,6 @@ bool TextureScalerCommon::ScaleInto(u32 *outputBuf, u32 *src, u32 &dstFmt, int &
}
// update values accordingly
dstFmt = Get8888Format();
width *= factor;
height *= factor;
@ -592,18 +578,18 @@ bool TextureScalerCommon::ScaleInto(u32 *outputBuf, u32 *src, u32 &dstFmt, int &
return true;
}
bool TextureScalerCommon::Scale(u32* &data, u32 &dstFmt, int &width, int &height, int factor) {
bool TextureScalerCommon::Scale(u32* &data, int &width, int &height, int factor) {
// prevent processing empty or flat textures (this happens a lot in some games)
// doesn't hurt the standard case, will be very quick for textures with actual texture
if (IsEmptyOrFlat(data, width*height, dstFmt)) {
if (IsEmptyOrFlat(data, width*height)) {
DEBUG_LOG(G3D, "TextureScaler: early exit -- empty/flat texture");
return false;
}
bufOutput.resize(width*height*factor*factor); // used to store the upscaled image
bufOutput.resize(width * height * (factor * factor)); // used to store the upscaled image
u32 *outputBuf = bufOutput.data();
if (ScaleInto(outputBuf, data, dstFmt, width, height, factor)) {
if (ScaleInto(outputBuf, data, width, height, factor)) {
data = outputBuf;
return true;
}

View File

@ -22,22 +22,21 @@
static const int MIN_TEXSCALE_LINES_PER_THREAD = 4;
// The texture scaler requires input to be in R8G8B8A8.
// (It's OK if you flip R and B as they are not treated very differently from each other.
// They will of course not unflip during the operation so be aware of that).
class TextureScalerCommon {
public:
TextureScalerCommon();
~TextureScalerCommon();
void ScaleAlways(u32 *out, u32 *src, u32 &dstFmt, int &width, int &height, int factor);
bool Scale(u32 *&data, u32 &dstfmt, int &width, int &height, int factor);
bool ScaleInto(u32 *out, u32 *src, u32 &dstfmt, int &width, int &height, int factor);
void ScaleAlways(u32 *out, u32 *src, int &width, int &height, int factor);
bool Scale(u32 *&data, int &width, int &height, int factor);
bool ScaleInto(u32 *out, u32 *src, int &width, int &height, int factor);
enum { XBRZ = 0, HYBRID = 1, BICUBIC = 2, HYBRID_BICUBIC = 3 };
protected:
virtual void ConvertTo8888(u32 format, u32 *source, u32 *&dest, int width, int height) = 0;
virtual int BytesPerPixel(u32 format) = 0;
virtual u32 Get8888Format() = 0;
void ScaleXBRZ(int factor, u32* source, u32* dest, int width, int height);
void ScaleBilinear(int factor, u32* source, u32* dest, int width, int height);
void ScaleBicubicBSpline(int factor, u32* source, u32* dest, int width, int height);
@ -46,10 +45,10 @@ protected:
void DePosterize(u32* source, u32* dest, int width, int height);
bool IsEmptyOrFlat(u32* data, int pixels, int fmt);
bool IsEmptyOrFlat(const u32 *data, int pixels) const;
// depending on the factor and texture sizes, these can get pretty large
// maximum is (100 MB total for a 512 by 512 texture with scaling factor 5 and hybrid scaling)
// of course, scaling factor 5 is totally silly anyway
SimpleBuf<u32> bufInput, bufDeposter, bufOutput, bufTmp1, bufTmp2, bufTmp3;
SimpleBuf<u32> bufDeposter, bufOutput, bufTmp1, bufTmp2, bufTmp3;
};

View File

@ -54,6 +54,20 @@ static const D3D11_INPUT_ELEMENT_DESC g_QuadVertexElements[] = {
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12,},
};
// NOTE: In the D3D backends, we flip R and B in the shaders, so while these look wrong, they're OK.
Draw::DataFormat FromD3D11Format(u32 fmt) {
switch (fmt) {
case DXGI_FORMAT_B8G8R8A8_UNORM: default: return Draw::DataFormat::R8G8B8A8_UNORM;
}
}
DXGI_FORMAT ToDXGIFormat(Draw::DataFormat fmt) {
switch (fmt) {
case Draw::DataFormat::R8G8B8A8_UNORM: default: return DXGI_FORMAT_B8G8R8A8_UNORM;
}
}
SamplerCacheD3D11::~SamplerCacheD3D11() {
for (auto &iter : cache_) {
iter.second->Release();
@ -224,7 +238,7 @@ void TextureCacheD3D11::BindTexture(TexCacheEntry *entry) {
context_->PSSetShaderResources(0, 1, &textureView);
lastBoundTexture = textureView;
}
int maxLevel = (entry->status & TexCacheEntry::STATUS_BAD_MIPS) ? 0 : entry->maxLevel;
int maxLevel = (entry->status & TexCacheEntry::STATUS_NO_MIPS) ? 0 : entry->maxLevel;
SamplerCacheKey samplerKey = GetSamplingParams(maxLevel, entry);
ID3D11SamplerState *state = samplerCache_.GetOrCreateSampler(device_, samplerKey);
context_->PSSetSamplers(0, 1, &state);
@ -434,119 +448,96 @@ void TextureCacheD3D11::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer,
}
void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) {
entry->status &= ~TexCacheEntry::STATUS_ALPHA_MASK;
// For the estimate, we assume cluts always point to 8888 for simplicity.
cacheSizeEstimate_ += EstimateTexMemoryUsage(entry);
if ((entry->bufw == 0 || (gstate.texbufwidth[0] & 0xf800) != 0) && entry->addr >= PSP_GetKernelMemoryEnd()) {
ERROR_LOG_REPORT(G3D, "Texture with unexpected bufw (full=%d)", gstate.texbufwidth[0] & 0xffff);
// Proceeding here can cause a crash.
BuildTexturePlan plan;
if (!PrepareBuildTexture(plan, entry)) {
// We're screwed?
return;
}
// Adjust maxLevel to actually present levels..
bool badMipSizes = false;
// maxLevel here is the max level to upload. Not the count.
int maxLevel = entry->maxLevel;
for (int i = 0; i <= maxLevel; i++) {
// If encountering levels pointing to nothing, adjust max level.
u32 levelTexaddr = gstate.getTextureAddress(i);
if (!Memory::IsValidAddress(levelTexaddr)) {
maxLevel = i - 1;
break;
}
// If size reaches 1, stop, and override maxlevel.
int tw = gstate.getTextureWidth(i);
int th = gstate.getTextureHeight(i);
if (tw == 1 || th == 1) {
maxLevel = i;
break;
}
if (i > 0 && gstate_c.Supports(GPU_SUPPORTS_TEXTURE_LOD_CONTROL)) {
if (tw != 1 && tw != (gstate.getTextureWidth(i - 1) >> 1))
badMipSizes = true;
else if (th != 1 && th != (gstate.getTextureHeight(i - 1) >> 1))
badMipSizes = true;
}
}
int scaleFactor = standardScaleFactor_;
// Rachet down scale factor in low-memory mode.
if (lowMemoryMode_) {
// Keep it even, though, just in case of npot troubles.
scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1);
}
int w = gstate.getTextureWidth(0);
int h = gstate.getTextureHeight(0);
ReplacedTexture &replaced = FindReplacement(entry, w, h);
if (replaced.Valid()) {
// We're replacing, so we won't scale.
scaleFactor = 1;
maxLevel = replaced.MaxLevel();
badMipSizes = false;
}
// Don't scale the PPGe texture.
if (entry->addr > 0x05000000 && entry->addr < PSP_GetKernelMemoryEnd())
scaleFactor = 1;
if ((entry->status & TexCacheEntry::STATUS_CHANGE_FREQUENT) != 0 && scaleFactor != 1) {
// Remember for later that we /wanted/ to scale this texture.
entry->status |= TexCacheEntry::STATUS_TO_SCALE;
scaleFactor = 1;
}
if (scaleFactor != 1) {
if (texelsScaledThisFrame_ >= TEXCACHE_MAX_TEXELS_SCALED) {
entry->status |= TexCacheEntry::STATUS_TO_SCALE;
scaleFactor = 1;
} else {
entry->status &= ~TexCacheEntry::STATUS_TO_SCALE;
entry->status |= TexCacheEntry::STATUS_IS_SCALED;
texelsScaledThisFrame_ += w * h;
}
}
// Seems to cause problems in Tactics Ogre.
if (badMipSizes) {
maxLevel = 0;
}
int tw = plan.w;
int th = plan.h;
DXGI_FORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat());
if (IsFakeMipmapChange()) {
// NOTE: Since the level is not part of the cache key, we assume it never changes.
u8 level = std::max(0, gstate.getTexLevelOffset16() / 16);
LoadTextureLevel(*entry, replaced, level, maxLevel, scaleFactor, dstFmt);
} else {
LoadTextureLevel(*entry, replaced, 0, maxLevel, scaleFactor, dstFmt);
if (plan.replaced->GetSize(plan.baseLevelSrc, tw, th)) {
dstFmt = ToDXGIFormat(plan.replaced->Format(plan.baseLevelSrc));
} else if (plan.scaleFactor > 1) {
tw *= plan.scaleFactor;
th *= plan.scaleFactor;
dstFmt = DXGI_FORMAT_B8G8R8A8_UNORM;
}
ID3D11ShaderResourceView *textureView = DxView(entry);
if (!textureView) {
return;
}
// We don't yet have mip generation, so clamp the number of levels to the ones we can load directly.
int levels = std::min(plan.levelsToCreate, plan.levelsToLoad);
// Mipmapping is only enabled when texture scaling is disabled.
if (maxLevel > 0 && scaleFactor == 1) {
for (int i = 1; i <= maxLevel; i++) {
LoadTextureLevel(*entry, replaced, i, maxLevel, scaleFactor, dstFmt);
D3D11_TEXTURE2D_DESC desc{};
desc.CPUAccessFlags = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.ArraySize = 1;
desc.SampleDesc.Count = 1;
desc.Width = tw;
desc.Height = th;
desc.Format = dstFmt;
desc.MipLevels = levels;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
ID3D11ShaderResourceView *view;
ID3D11Texture2D *texture = DxTex(entry);
_assert_(texture == nullptr);
ASSERT_SUCCESS(device_->CreateTexture2D(&desc, nullptr, &texture));
ASSERT_SUCCESS(device_->CreateShaderResourceView(texture, nullptr, &view));
entry->texturePtr = texture;
entry->textureView = view;
Draw::DataFormat texFmt = FromD3D11Format(dstFmt);
for (int i = 0; i < levels; i++) {
int srcLevel = (i == 0) ? plan.baseLevelSrc : i;
int w = gstate.getTextureWidth(srcLevel);
int h = gstate.getTextureHeight(srcLevel);
u8 *data = nullptr;
int stride = 0;
// For UpdateSubresource, we can't decode directly into the texture so we allocate a buffer :(
// NOTE: Could reuse it between levels or textures!
if (plan.replaced->GetSize(srcLevel, w, h)) {
int bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel));
stride = w * bpp;
data = (u8 *)AllocateAlignedMemory(stride * h, 16);
} else {
if (plan.scaleFactor > 1) {
data = (u8 *)AllocateAlignedMemory(4 * (w * plan.scaleFactor) * (h * plan.scaleFactor), 16);
stride = w * plan.scaleFactor * 4;
} else {
int bpp = dstFmt == DXGI_FORMAT_B8G8R8A8_UNORM ? 4 : 2;
stride = std::max(w * bpp, 16);
data = (u8 *)AllocateAlignedMemory(stride * h, 16);
}
}
if (!data) {
ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", w, h);
return;
}
LoadTextureLevel(*entry, data, stride, *plan.replaced, srcLevel, plan.scaleFactor, texFmt, false);
ID3D11Texture2D *texture = DxTex(entry);
context_->UpdateSubresource(texture, i, nullptr, data, stride, 0);
FreeAlignedMemory(data);
}
if (maxLevel == 0) {
entry->status |= TexCacheEntry::STATUS_BAD_MIPS;
if (levels == 1) {
entry->status |= TexCacheEntry::STATUS_NO_MIPS;
} else {
entry->status &= ~TexCacheEntry::STATUS_BAD_MIPS;
entry->status &= ~TexCacheEntry::STATUS_NO_MIPS;
}
if (replaced.Valid()) {
entry->SetAlphaStatus(TexCacheEntry::TexStatus(replaced.AlphaStatus()));
if (plan.replaced->Valid()) {
entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus()));
}
}
@ -605,149 +596,6 @@ CheckAlphaResult TextureCacheD3D11::CheckAlpha(const u32 *pixelData, u32 dstFmt,
}
}
ReplacedTextureFormat FromD3D11Format(u32 fmt) {
switch (fmt) {
case DXGI_FORMAT_B5G6R5_UNORM: return ReplacedTextureFormat::F_5650;
case DXGI_FORMAT_B5G5R5A1_UNORM: return ReplacedTextureFormat::F_5551;
case DXGI_FORMAT_B4G4R4A4_UNORM: return ReplacedTextureFormat::F_4444;
case DXGI_FORMAT_B8G8R8A8_UNORM: default: return ReplacedTextureFormat::F_8888;
}
}
DXGI_FORMAT ToDXGIFormat(ReplacedTextureFormat fmt) {
switch (fmt) {
case ReplacedTextureFormat::F_5650: return DXGI_FORMAT_B5G6R5_UNORM;
case ReplacedTextureFormat::F_5551: return DXGI_FORMAT_B5G5R5A1_UNORM;
case ReplacedTextureFormat::F_4444: return DXGI_FORMAT_B4G4R4A4_UNORM;
case ReplacedTextureFormat::F_8888: default: return DXGI_FORMAT_B8G8R8A8_UNORM;
}
}
void TextureCacheD3D11::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &replaced, int level, int maxLevel, int scaleFactor, DXGI_FORMAT dstFmt) {
int w = gstate.getTextureWidth(level);
int h = gstate.getTextureHeight(level);
ID3D11Texture2D *texture = DxTex(&entry);
if ((level == 0 || IsFakeMipmapChange()) && texture == nullptr) {
// Create texture
int levels = scaleFactor == 1 ? maxLevel + 1 : 1;
int tw = w, th = h;
DXGI_FORMAT tfmt = dstFmt;
if (replaced.GetSize(level, tw, th)) {
tfmt = ToDXGIFormat(replaced.Format(level));
} else {
tw *= scaleFactor;
th *= scaleFactor;
if (scaleFactor > 1) {
tfmt = DXGI_FORMAT_B8G8R8A8_UNORM;
}
}
D3D11_TEXTURE2D_DESC desc{};
// TODO: Make it DEFAULT or immutable, required for multiple mip levels. Will require some code restructuring though.
desc.CPUAccessFlags = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.ArraySize = 1;
desc.SampleDesc.Count = 1;
desc.Width = tw;
desc.Height = th;
desc.Format = tfmt;
desc.MipLevels = IsFakeMipmapChange() ? 1 : levels;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
ASSERT_SUCCESS(device_->CreateTexture2D(&desc, nullptr, &texture));
ID3D11ShaderResourceView *view;
ASSERT_SUCCESS(device_->CreateShaderResourceView(texture, nullptr, &view));
entry.texturePtr = texture;
entry.textureView = view;
}
gpuStats.numTexturesDecoded++;
// For UpdateSubresource, we can't decode directly into the texture so we allocate a buffer :(
u32 *mapData = nullptr;
int mapRowPitch = 0;
if (replaced.GetSize(level, w, h)) {
mapData = (u32 *)AllocateAlignedMemory(w * h * sizeof(u32), 16);
mapRowPitch = w * 4;
double replaceStart = time_now_d();
replaced.Load(level, mapData, mapRowPitch);
replacementTimeThisFrame_ += time_now_d() - replaceStart;
dstFmt = ToDXGIFormat(replaced.Format(level));
} else {
GETextureFormat tfmt = (GETextureFormat)entry.format;
GEPaletteFormat clutformat = gstate.getClutPaletteFormat();
u32 texaddr = gstate.getTextureAddress(level);
int bufw = GetTextureBufw(level, texaddr, tfmt);
int bpp = dstFmt == DXGI_FORMAT_B8G8R8A8_UNORM ? 4 : 2;
u32 *pixelData;
int decPitch;
if (scaleFactor > 1) {
tmpTexBufRearrange_.resize(std::max(bufw, w) * h);
pixelData = tmpTexBufRearrange_.data();
// We want to end up with a neatly packed texture for scaling.
decPitch = w * bpp;
mapData = (u32 *)AllocateAlignedMemory(sizeof(u32) * (w * scaleFactor) * (h * scaleFactor), 16);
mapRowPitch = w * scaleFactor * 4;
} else {
mapRowPitch = std::max(w * bpp, 16);
size_t bufSize = sizeof(u32) * (mapRowPitch / bpp) * h;
mapData = (u32 *)AllocateAlignedMemory(bufSize, 16);
if (!mapData) {
ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (alloc size: %lld, %dx%d)", (unsigned long long)bufSize, mapRowPitch / (int)sizeof(u32), h);
return;
}
pixelData = (u32 *)mapData;
decPitch = mapRowPitch;
}
bool expand32 = !gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS);
CheckAlphaResult alphaResult = DecodeTextureLevel((u8 *)pixelData, decPitch, tfmt, clutformat, texaddr, level, bufw, false, false, expand32);
entry.SetAlphaStatus(alphaResult, level);
if (scaleFactor > 1) {
u32 scaleFmt = (u32)dstFmt;
scaler.ScaleAlways((u32 *)mapData, pixelData, scaleFmt, w, h, scaleFactor);
pixelData = (u32 *)mapData;
// We always end up at 8888. Other parts assume this.
_assert_(scaleFmt == DXGI_FORMAT_B8G8R8A8_UNORM);
bpp = sizeof(u32);
decPitch = w * bpp;
if (decPitch != mapRowPitch) {
// Rearrange in place to match the requested pitch.
// (it can only be larger than w * bpp, and a match is likely.)
// Note! This is bad because it reads the mapped memory! TODO: Look into if DX9 does this right.
for (int y = h - 1; y >= 0; --y) {
memcpy((u8 *)mapData + mapRowPitch * y, (u8 *)mapData + decPitch * y, w * bpp);
}
decPitch = mapRowPitch;
}
}
if (replacer_.Enabled()) {
ReplacedTextureDecodeInfo replacedInfo;
replacedInfo.cachekey = entry.CacheKey();
replacedInfo.hash = entry.fullhash;
replacedInfo.addr = entry.addr;
replacedInfo.isVideo = IsVideo(entry.addr);
replacedInfo.isFinal = (entry.status & TexCacheEntry::STATUS_TO_SCALE) == 0;
replacedInfo.scaleFactor = scaleFactor;
replacedInfo.fmt = FromD3D11Format(dstFmt);
// NOTE: Reading the decoded texture here may be very slow, if we just wrote it to write-combined memory.
replacer_.NotifyTextureDecoded(replacedInfo, pixelData, decPitch, level, w, h);
}
}
if (IsFakeMipmapChange())
context_->UpdateSubresource(texture, 0, nullptr, mapData, mapRowPitch, 0);
else
context_->UpdateSubresource(texture, level, nullptr, mapData, mapRowPitch, 0);
FreeAlignedMemory(mapData);
}
bool TextureCacheD3D11::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) {
SetTexture();
if (!nextTexture_) {

View File

@ -23,7 +23,6 @@
#include "GPU/GPU.h"
#include "GPU/GPUInterface.h"
#include "GPU/D3D11/TextureScalerD3D11.h"
#include "GPU/Common/TextureCacheCommon.h"
struct VirtualFramebuffer;
@ -68,7 +67,6 @@ protected:
void ReleaseTexture(TexCacheEntry *entry, bool delete_them) override;
private:
void LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &replaced, int level, int maxLevel, int scaleFactor, DXGI_FORMAT dstFmt);
DXGI_FORMAT GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const;
static CheckAlphaResult CheckAlpha(const u32 *pixelData, u32 dstFmt, int w);
void UpdateCurrentClut(GEPaletteFormat clutFormat, u32 clutBase, bool clutIndexIsSimple) override;
@ -86,8 +84,6 @@ private:
return (ID3D11ShaderResourceView *)entry->textureView;
}
TextureScalerD3D11 scaler;
SamplerCacheD3D11 samplerCache_;
ID3D11ShaderResourceView *lastBoundTexture;

View File

@ -1,58 +0,0 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <algorithm>
#include <d3d11.h>
#include "Common/Data/Convert/ColorConv.h"
#include "Core/ThreadPools.h"
#include "Common/Thread/ParallelLoop.h"
#include "GPU/Common/TextureScalerCommon.h"
#include "GPU/D3D11/TextureScalerD3D11.h"
#include "GPU/D3D11/GPU_D3D11.h"
int TextureScalerD3D11::BytesPerPixel(u32 format) {
return format == DXGI_FORMAT_B8G8R8A8_UNORM ? 4 : 2;
}
u32 TextureScalerD3D11::Get8888Format() {
return DXGI_FORMAT_B8G8R8A8_UNORM;
}
void TextureScalerD3D11::ConvertTo8888(u32 format, u32* source, u32* &dest, int width, int height) {
switch (format) {
case DXGI_FORMAT_B8G8R8A8_UNORM:
dest = source; // already fine
break;
case DXGI_FORMAT_B4G4R4A4_UNORM:
ParallelRangeLoop(&g_threadManager, std::bind(&convert4444_dx9, (u16*)source, dest, width, std::placeholders::_1, std::placeholders::_2), 0, height, MIN_TEXSCALE_LINES_PER_THREAD);
break;
case DXGI_FORMAT_B5G6R5_UNORM:
ParallelRangeLoop(&g_threadManager, std::bind(&convert565_dx9, (u16*)source, dest, width, std::placeholders::_1, std::placeholders::_2), 0, height, MIN_TEXSCALE_LINES_PER_THREAD);
break;
case DXGI_FORMAT_B5G5R5A1_UNORM:
ParallelRangeLoop(&g_threadManager, std::bind(&convert5551_dx9, (u16*)source, dest, width, std::placeholders::_1, std::placeholders::_2), 0, height, MIN_TEXSCALE_LINES_PER_THREAD);
break;
default:
dest = source;
ERROR_LOG(G3D, "iXBRZTexScaling: unsupported texture format");
}
}

View File

@ -1,29 +0,0 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include "Common/CommonTypes.h"
#include "GPU/Common/TextureScalerCommon.h"
class TextureScalerD3D11 : public TextureScalerCommon {
private:
// NOTE: We use GE formats, D3D11 doesn't support 4444
void ConvertTo8888(u32 format, u32* source, u32* &dest, int width, int height) override;
int BytesPerPixel(u32 format) override;
u32 Get8888Format() override;
};

View File

@ -598,7 +598,9 @@ rotateVBO:
if (result.action == SW_DRAW_PRIMITIVES) {
if (result.setStencil) {
dxstate.stencilFunc.set(D3DCMP_ALWAYS, result.stencilValue, 255);
dxstate.stencilFunc.set(D3DCMP_ALWAYS);
dxstate.stencilRef.set(result.stencilValue);
dxstate.stencilCompareMask.set(255);
}
// TODO: Add a post-transform cache here for multi-RECTANGLES only.

View File

@ -200,12 +200,12 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
dxstate.texMipLodBias.set(0.0f);
dxstate.texMaxMipLevel.set(0);
dxstate.blend.disable();
dxstate.cullMode.set(false, false);
dxstate.cullMode.set(D3DCULL_NONE);
dxstate.depthTest.disable();
dxstate.scissorTest.disable();
dxstate.stencilTest.disable();
dxstate.colorMask.set(true, true, true, true);
dxstate.stencilMask.set(0xFF);
dxstate.colorMask.set(0xF);
dxstate.stencilWriteMask.set(0xFF);
HRESULT hr = device_->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, coord, 5 * sizeof(float));
if (FAILED(hr)) {
ERROR_LOG_REPORT(G3D, "DrawActiveTexture() failed: %08x", (uint32_t)hr);

View File

@ -256,7 +256,7 @@ void GPU_DX9::DeviceRestore() {
void GPU_DX9::InitClear() {
if (!framebufferManager_->UseBufferedRendering()) {
dxstate.depthWrite.set(true);
dxstate.colorMask.set(true, true, true, true);
dxstate.colorMask.set(0xF);
device_->Clear(0, NULL, D3DCLEAR_STENCIL|D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.f, 0);
}
}
@ -298,7 +298,7 @@ void GPU_DX9::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat for
void GPU_DX9::CopyDisplayToOutput(bool reallyDirty) {
dxstate.depthWrite.set(true);
dxstate.colorMask.set(true, true, true, true);
dxstate.colorMask.set(0xF);
drawEngine_.Flush();

View File

@ -119,11 +119,15 @@ void DrawEngineDX9::ApplyDrawState(int prim) {
gstate_c.SetAllowFramebufferRead(false);
if (gstate.isModeClear()) {
dxstate.blend.disable();
// Color Mask
bool colorMask = gstate.isClearModeColorMask();
bool alphaMask = gstate.isClearModeAlphaMask();
dxstate.colorMask.set(colorMask, colorMask, colorMask, alphaMask);
u32 mask = 0;
if (gstate.isClearModeColorMask()) {
mask |= 7;
}
if (gstate.isClearModeAlphaMask()) {
mask |= 8;
}
dxstate.colorMask.set(mask);
} else {
GenericMaskState maskState;
ConvertMaskState(maskState, gstate_c.allowFramebufferRead);
@ -158,20 +162,27 @@ void DrawEngineDX9::ApplyDrawState(int prim) {
dxstate.blend.disable();
}
dxstate.colorMask.set(maskState.rgba[0], maskState.rgba[1], maskState.rgba[2], maskState.rgba[3]);
u32 mask = 0;
for (int i = 0; i < 4; i++) {
if (maskState.rgba[i])
mask |= 1 << i;
}
dxstate.colorMask.set(mask);
}
}
if (gstate_c.IsDirty(DIRTY_RASTER_STATE)) {
gstate_c.Clean(DIRTY_RASTER_STATE);
// Set Dither
if (gstate.isDitherEnabled()) {
dxstate.dither.enable();
} else {
dxstate.dither.disable();
}
bool wantCull = !gstate.isModeClear() && prim != GE_PRIM_RECTANGLES && prim > GE_PRIM_LINE_STRIP && gstate.isCullEnabled();
dxstate.cullMode.set(wantCull, gstate.getCullMode());
if (wantCull) {
if (gstate.getCullMode() == 1) {
dxstate.cullMode.set(D3DCULL_CCW);
} else {
dxstate.cullMode.set(D3DCULL_CW);
}
} else {
dxstate.cullMode.set(D3DCULL_NONE);
}
if (gstate.isModeClear()) {
// Well, probably doesn't matter...
dxstate.shadeMode.set(D3DSHADE_GOURAUD);
@ -200,8 +211,10 @@ void DrawEngineDX9::ApplyDrawState(int prim) {
if (alphaMask) {
dxstate.stencilTest.enable();
dxstate.stencilOp.set(D3DSTENCILOP_REPLACE, D3DSTENCILOP_REPLACE, D3DSTENCILOP_REPLACE);
dxstate.stencilFunc.set(D3DCMP_ALWAYS, 255, 0xFF);
dxstate.stencilMask.set(stencilState.writeMask);
dxstate.stencilFunc.set(D3DCMP_ALWAYS);
dxstate.stencilRef.set(0xFF);
dxstate.stencilCompareMask.set(0xFF);
dxstate.stencilWriteMask.set(stencilState.writeMask);
} else {
dxstate.stencilTest.disable();
}
@ -222,9 +235,11 @@ void DrawEngineDX9::ApplyDrawState(int prim) {
// Stencil Test
if (stencilState.enabled) {
dxstate.stencilTest.enable();
dxstate.stencilFunc.set(ztests[stencilState.testFunc], stencilState.testRef, stencilState.testMask);
dxstate.stencilFunc.set(ztests[stencilState.testFunc]);
dxstate.stencilRef.set(stencilState.testRef);
dxstate.stencilCompareMask.set(stencilState.testMask);
dxstate.stencilOp.set(stencilOps[stencilState.sFail], stencilOps[stencilState.zFail], stencilOps[stencilState.zPass]);
dxstate.stencilMask.set(stencilState.writeMask);
dxstate.stencilWriteMask.set(stencilState.writeMask);
} else {
dxstate.stencilTest.disable();
}

View File

@ -117,7 +117,7 @@ bool FramebufferManagerDX9::NotifyStencilUpload(u32 addr, int size, StencilUploa
// Let's not bother with the shader if it's just zero.
dxstate.scissorTest.disable();
dxstate.colorMask.set(false, false, false, true);
dxstate.colorMask.set(0x8);
// TODO: Verify this clears only stencil/alpha.
device_->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_STENCIL, D3DCOLOR_RGBA(0, 0, 0, 0), 0.0f, 0);
@ -181,7 +181,7 @@ bool FramebufferManagerDX9::NotifyStencilUpload(u32 addr, int size, StencilUploa
shaderManager_->DirtyLastShader();
dxstate.colorMask.set(false, false, false, true);
dxstate.colorMask.set(0x8);
dxstate.stencilTest.enable();
dxstate.stencilOp.set(D3DSTENCILOP_REPLACE, D3DSTENCILOP_REPLACE, D3DSTENCILOP_REPLACE);
@ -202,7 +202,9 @@ bool FramebufferManagerDX9::NotifyStencilUpload(u32 addr, int size, StencilUploa
// TODO: Ideally, we should clear alpha to zero here (but not RGB.)
dxstate.stencilFunc.set(D3DCMP_ALWAYS, 0xFF, 0xFF);
dxstate.stencilFunc.set(D3DCMP_ALWAYS);
dxstate.stencilRef.set(0xFF);
dxstate.stencilCompareMask.set(0xFF);
float coord[20] = {
-1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
@ -228,15 +230,15 @@ bool FramebufferManagerDX9::NotifyStencilUpload(u32 addr, int size, StencilUploa
continue;
}
if (dstBuffer->format == GE_FORMAT_4444) {
dxstate.stencilMask.set(i | (i << 4));
dxstate.stencilWriteMask.set(i | (i << 4));
const float f[4] = {i * (16.0f / 255.0f)};
device_->SetPixelShaderConstantF(CONST_PS_STENCILVALUE, f, 1);
} else if (dstBuffer->format == GE_FORMAT_5551) {
dxstate.stencilMask.set(0xFF);
dxstate.stencilWriteMask.set(0xFF);
const float f[4] = {i * (128.0f / 255.0f)};
device_->SetPixelShaderConstantF(CONST_PS_STENCILVALUE, f, 1);
} else {
dxstate.stencilMask.set(i);
dxstate.stencilWriteMask.set(i);
const float f[4] = {i * (1.0f / 255.0f)};
device_->SetPixelShaderConstantF(CONST_PS_STENCILVALUE, f, 1);
}
@ -247,7 +249,7 @@ bool FramebufferManagerDX9::NotifyStencilUpload(u32 addr, int size, StencilUploa
}
tex->Release();
dxstate.stencilMask.set(0xFF);
dxstate.stencilWriteMask.set(0xFF);
dxstate.viewport.restore();
RebindFramebuffer("RebindFramebuffer stencil");
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS);

View File

@ -37,6 +37,19 @@
#include "ext/xxhash.h"
#include "Common/Math/math_util.h"
// NOTE: In the D3D backends, we flip R and B in the shaders, so while these look wrong, they're OK.
Draw::DataFormat FromD3D9Format(u32 fmt) {
switch (fmt) {
case D3DFMT_A8R8G8B8: default: return Draw::DataFormat::R8G8B8A8_UNORM;
}
}
D3DFORMAT ToD3D9Format(Draw::DataFormat fmt) {
switch (fmt) {
case Draw::DataFormat::R8G8B8A8_UNORM: default: return D3DFMT_A8R8G8B8;
}
}
namespace DX9 {
@ -112,7 +125,8 @@ D3DFORMAT getClutDestFormat(GEPaletteFormat format) {
}
void TextureCacheDX9::ApplySamplingParams(const SamplerCacheKey &key) {
dxstate.texMinFilter.set(key.minFilt ? D3DTEXF_LINEAR : D3DTEXF_POINT);
D3DTEXTUREFILTERTYPE minFilt = (false ? D3DTEXF_ANISOTROPIC : D3DTEXF_LINEAR);
dxstate.texMinFilter.set(key.minFilt ? minFilt : D3DTEXF_POINT);
dxstate.texMipFilter.set(key.mipFilt ? D3DTEXF_LINEAR : D3DTEXF_POINT);
dxstate.texMagFilter.set(key.magFilt ? D3DTEXF_LINEAR : D3DTEXF_POINT);
@ -196,7 +210,7 @@ void TextureCacheDX9::BindTexture(TexCacheEntry *entry) {
device_->SetTexture(0, texture);
lastBoundTexture = texture;
}
int maxLevel = (entry->status & TexCacheEntry::STATUS_BAD_MIPS) ? 0 : entry->maxLevel;
int maxLevel = (entry->status & TexCacheEntry::STATUS_NO_MIPS) ? 0 : entry->maxLevel;
SamplerCacheKey samplerKey = GetSamplingParams(maxLevel, entry);
ApplySamplingParams(samplerKey);
}
@ -304,6 +318,7 @@ public:
}
void Shade() {
// Intentionally bypassing the dxstate cache here (and using .Restore to recover afterwards). Not sure if this is a good idea.
device_->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
device_->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, FALSE);
device_->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA);
@ -318,7 +333,6 @@ public:
if (FAILED(hr)) {
ERROR_LOG_REPORT(G3D, "Depal render failed: %08x", (uint32_t)hr);
}
dxstate.Restore();
}
@ -390,116 +404,67 @@ void TextureCacheDX9::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer, G
}
void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) {
entry->status &= ~TexCacheEntry::STATUS_ALPHA_MASK;
// For the estimate, we assume cluts always point to 8888 for simplicity.
cacheSizeEstimate_ += EstimateTexMemoryUsage(entry);
if ((entry->bufw == 0 || (gstate.texbufwidth[0] & 0xf800) != 0) && entry->addr >= PSP_GetKernelMemoryEnd()) {
ERROR_LOG_REPORT(G3D, "Texture with unexpected bufw (full=%d)", gstate.texbufwidth[0] & 0xffff);
// Proceeding here can cause a crash.
BuildTexturePlan plan;
if (!PrepareBuildTexture(plan, entry)) {
// We're screwed?
return;
}
// Adjust maxLevel to actually present levels..
bool badMipSizes = false;
int maxLevel = entry->maxLevel;
for (int i = 0; i <= maxLevel; i++) {
// If encountering levels pointing to nothing, adjust max level.
u32 levelTexaddr = gstate.getTextureAddress(i);
if (!Memory::IsValidAddress(levelTexaddr)) {
maxLevel = i - 1;
break;
}
int tw = plan.w;
int th = plan.h;
// If size reaches 1, stop, and override maxlevel.
int tw = gstate.getTextureWidth(i);
int th = gstate.getTextureHeight(i);
if (tw == 1 || th == 1) {
maxLevel = i;
break;
}
if (i > 0 && gstate_c.Supports(GPU_SUPPORTS_TEXTURE_LOD_CONTROL)) {
if (tw != 1 && tw != (gstate.getTextureWidth(i - 1) >> 1))
badMipSizes = true;
else if (th != 1 && th != (gstate.getTextureHeight(i - 1) >> 1))
badMipSizes = true;
}
}
// If GLES3 is available, we can preallocate the storage, which makes texture loading more efficient.
D3DFORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat());
int scaleFactor = standardScaleFactor_;
// Rachet down scale factor in low-memory mode.
if (lowMemoryMode_) {
// Keep it even, though, just in case of npot troubles.
scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1);
if (plan.replaced->GetSize(plan.baseLevelSrc, tw, th)) {
dstFmt = ToD3D9Format(plan.replaced->Format(plan.baseLevelSrc));
} else if (plan.scaleFactor > 1) {
tw *= plan.scaleFactor;
th *= plan.scaleFactor;
dstFmt = D3DFMT_A8R8G8B8;
}
int w = gstate.getTextureWidth(0);
int h = gstate.getTextureHeight(0);
ReplacedTexture &replaced = FindReplacement(entry, w, h);
if (replaced.Valid()) {
// We're replacing, so we won't scale.
scaleFactor = 1;
maxLevel = replaced.MaxLevel();
badMipSizes = false;
}
// Don't scale the PPGe texture.
if (entry->addr > 0x05000000 && entry->addr < PSP_GetKernelMemoryEnd())
scaleFactor = 1;
if ((entry->status & TexCacheEntry::STATUS_CHANGE_FREQUENT) != 0 && scaleFactor != 1) {
// Remember for later that we /wanted/ to scale this texture.
entry->status |= TexCacheEntry::STATUS_TO_SCALE;
scaleFactor = 1;
}
if (scaleFactor != 1) {
if (texelsScaledThisFrame_ >= TEXCACHE_MAX_TEXELS_SCALED) {
entry->status |= TexCacheEntry::STATUS_TO_SCALE;
scaleFactor = 1;
} else {
entry->status &= ~TexCacheEntry::STATUS_TO_SCALE;
entry->status |= TexCacheEntry::STATUS_IS_SCALED;
texelsScaledThisFrame_ += w * h;
}
}
// Seems to cause problems in Tactics Ogre.
if (badMipSizes) {
maxLevel = 0;
}
u8 level = 0;
if (IsFakeMipmapChange()) {
// NOTE: Since the level is not part of the cache key, we assume it never changes.
level = std::max(0, gstate.getTexLevelOffset16() / 16);
}
LoadTextureLevel(*entry, replaced, level, maxLevel, scaleFactor, dstFmt);
// We don't yet have mip generation, so clamp the number of levels to the ones we can load directly.
int levels = std::min(plan.levelsToCreate, plan.levelsToLoad);
LPDIRECT3DTEXTURE9 &texture = DxTex(entry);
if (!texture) {
D3DPOOL pool = D3DPOOL_DEFAULT;
int usage = D3DUSAGE_DYNAMIC;
HRESULT hr = device_->CreateTexture(tw, th, levels, usage, dstFmt, pool, &texture, NULL);
if (FAILED(hr)) {
INFO_LOG(G3D, "Failed to create D3D texture: %dx%d", tw, th);
ReleaseTexture(entry, true);
return;
}
// Mipmapping is only enabled when texture scaling is disabled.
if (maxLevel > 0 && scaleFactor == 1) {
for (int i = 1; i <= maxLevel; i++) {
LoadTextureLevel(*entry, replaced, i, maxLevel, scaleFactor, dstFmt);
}
if (!texture) {
// What to do here?
return;
}
if (maxLevel == 0) {
entry->status |= TexCacheEntry::STATUS_BAD_MIPS;
} else {
entry->status &= ~TexCacheEntry::STATUS_BAD_MIPS;
Draw::DataFormat texFmt = FromD3D9Format(dstFmt);
// Mipmapping is only enabled when texture scaling is disabled.
for (int i = 0; i < levels; i++) {
int dstLevel = i;
HRESULT result;
uint32_t lockFlag = dstLevel == 0 ? D3DLOCK_DISCARD : 0; // Can only discard the top level
D3DLOCKED_RECT rect{};
result = texture->LockRect(dstLevel, &rect, NULL, lockFlag);
if (FAILED(result)) {
ERROR_LOG(G3D, "Failed to lock D3D texture at level %d: %dx%d", i, plan.w, plan.h);
return;
}
uint8_t *data = (uint8_t *)rect.pBits;
int stride = rect.Pitch;
LoadTextureLevel(*entry, data, stride, *plan.replaced, (i == 0) ? plan.baseLevelSrc : i, plan.scaleFactor, texFmt, false);
texture->UnlockRect(dstLevel);
}
if (replaced.Valid()) {
entry->SetAlphaStatus(TexCacheEntry::TexStatus(replaced.AlphaStatus()));
if (plan.replaced->Valid()) {
entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus()));
}
}
@ -539,136 +504,6 @@ CheckAlphaResult TextureCacheDX9::CheckAlpha(const u32 *pixelData, u32 dstFmt, i
}
}
ReplacedTextureFormat FromD3D9Format(u32 fmt) {
switch (fmt) {
case D3DFMT_R5G6B5: return ReplacedTextureFormat::F_5650;
case D3DFMT_A1R5G5B5: return ReplacedTextureFormat::F_5551;
case D3DFMT_A4R4G4B4: return ReplacedTextureFormat::F_4444;
case D3DFMT_A8R8G8B8: default: return ReplacedTextureFormat::F_8888;
}
}
D3DFORMAT ToD3D9Format(ReplacedTextureFormat fmt) {
switch (fmt) {
case ReplacedTextureFormat::F_5650: return D3DFMT_R5G6B5;
case ReplacedTextureFormat::F_5551: return D3DFMT_A1R5G5B5;
case ReplacedTextureFormat::F_4444: return D3DFMT_A4R4G4B4;
case ReplacedTextureFormat::F_8888: default: return D3DFMT_A8R8G8B8;
}
}
void TextureCacheDX9::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &replaced, int level, int maxLevel, int scaleFactor, u32 dstFmt) {
int w = gstate.getTextureWidth(level);
int h = gstate.getTextureHeight(level);
LPDIRECT3DTEXTURE9 &texture = DxTex(&entry);
if ((level == 0 || IsFakeMipmapChange()) && texture == nullptr) {
// Create texture
D3DPOOL pool = D3DPOOL_MANAGED;
int usage = 0;
pool = D3DPOOL_DEFAULT;
usage = D3DUSAGE_DYNAMIC; // TODO: Switch to using a staging texture?
int levels = scaleFactor == 1 ? maxLevel + 1 : 1;
int tw = w, th = h;
D3DFORMAT tfmt = (D3DFORMAT)(dstFmt);
if (replaced.GetSize(level, tw, th)) {
tfmt = ToD3D9Format(replaced.Format(level));
} else {
tw *= scaleFactor;
th *= scaleFactor;
if (scaleFactor > 1) {
tfmt = D3DFMT_A8R8G8B8;
}
}
HRESULT hr;
if (IsFakeMipmapChange())
hr = device_->CreateTexture(tw, th, 1, usage, tfmt, pool, &texture, NULL);
else
hr = device_->CreateTexture(tw, th, levels, usage, tfmt, pool, &texture, NULL);
if (FAILED(hr)) {
INFO_LOG(G3D, "Failed to create D3D texture: %dx%d", tw, th);
ReleaseTexture(&entry, true);
return;
}
}
D3DLOCKED_RECT rect;
HRESULT result;
uint32_t lockFlag = level == 0 ? D3DLOCK_DISCARD : 0; // Can only discard the top level
if (IsFakeMipmapChange())
result = texture->LockRect(0, &rect, NULL, lockFlag);
else
result = texture->LockRect(level, &rect, NULL, lockFlag);
if (FAILED(result)) {
ERROR_LOG(G3D, "Failed to lock D3D texture: %dx%d", w, h);
return;
}
gpuStats.numTexturesDecoded++;
if (replaced.GetSize(level, w, h)) {
double replaceStart = time_now_d();
replaced.Load(level, rect.pBits, rect.Pitch);
replacementTimeThisFrame_ += time_now_d() - replaceStart;
dstFmt = ToD3D9Format(replaced.Format(level));
} else {
GETextureFormat tfmt = (GETextureFormat)entry.format;
GEPaletteFormat clutformat = gstate.getClutPaletteFormat();
u32 texaddr = gstate.getTextureAddress(level);
int bufw = GetTextureBufw(level, texaddr, tfmt);
int bpp = dstFmt == D3DFMT_A8R8G8B8 ? 4 : 2;
u32 *pixelData = (u32 *)rect.pBits;
int decPitch = rect.Pitch;
if (scaleFactor > 1) {
tmpTexBufRearrange_.resize(std::max(bufw, w) * h);
pixelData = tmpTexBufRearrange_.data();
// We want to end up with a neatly packed texture for scaling.
decPitch = w * bpp;
}
CheckAlphaResult alphaResult = DecodeTextureLevel((u8 *)pixelData, decPitch, tfmt, clutformat, texaddr, level, bufw, false, false, false);
entry.SetAlphaStatus(alphaResult, level);
if (scaleFactor > 1) {
scaler.ScaleAlways((u32 *)rect.pBits, pixelData, dstFmt, w, h, scaleFactor);
pixelData = (u32 *)rect.pBits;
// We always end up at 8888. Other parts assume this.
_assert_(dstFmt == D3DFMT_A8R8G8B8);
bpp = sizeof(u32);
decPitch = w * bpp;
if (decPitch != rect.Pitch) {
// Rearrange in place to match the requested pitch.
// (it can only be larger than w * bpp, and a match is likely.)
for (int y = h - 1; y >= 0; --y) {
memcpy((u8 *)rect.pBits + rect.Pitch * y, (u8 *)rect.pBits + decPitch * y, w * bpp);
}
decPitch = rect.Pitch;
}
}
if (replacer_.Enabled()) {
ReplacedTextureDecodeInfo replacedInfo;
replacedInfo.cachekey = entry.CacheKey();
replacedInfo.hash = entry.fullhash;
replacedInfo.addr = entry.addr;
replacedInfo.isVideo = IsVideo(entry.addr);
replacedInfo.isFinal = (entry.status & TexCacheEntry::STATUS_TO_SCALE) == 0;
replacedInfo.scaleFactor = scaleFactor;
replacedInfo.fmt = FromD3D9Format(dstFmt);
replacer_.NotifyTextureDecoded(replacedInfo, pixelData, decPitch, level, w, h);
}
}
if (IsFakeMipmapChange())
texture->UnlockRect(0);
else
texture->UnlockRect(level);
}
bool TextureCacheDX9::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) {
SetTexture();
ApplyTexture();

View File

@ -21,7 +21,6 @@
#include "GPU/GPU.h"
#include "GPU/GPUInterface.h"
#include "GPU/Directx9/TextureScalerDX9.h"
#include "GPU/Common/TextureCacheCommon.h"
struct VirtualFramebuffer;
@ -62,7 +61,6 @@ protected:
private:
void ApplySamplingParams(const SamplerCacheKey &key);
void LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &replaced, int level, int maxLevel, int scaleFactor, u32 dstFmt);
D3DFORMAT GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const;
static CheckAlphaResult CheckAlpha(const u32 *pixelData, u32 dstFmt, int w);
void UpdateCurrentClut(GEPaletteFormat clutFormat, u32 clutBase, bool clutIndexIsSimple) override;
@ -77,8 +75,6 @@ private:
LPDIRECT3DDEVICE9 device_;
LPDIRECT3DDEVICE9EX deviceEx_;
TextureScalerDX9 scaler;
LPDIRECT3DVERTEXDECLARATION9 pFramebufferVertexDecl;
LPDIRECT3DTEXTURE9 lastBoundTexture;

View File

@ -1,62 +0,0 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <algorithm>
#include "Common/CommonTypes.h"
#include "Common/Data/Convert/ColorConv.h"
#include "Common/Thread/ParallelLoop.h"
#include "Core/ThreadPools.h"
#include "GPU/Common/TextureScalerCommon.h"
#include "GPU/Directx9/TextureScalerDX9.h"
#include "GPU/Directx9/GPU_DX9.h"
namespace DX9 {
int TextureScalerDX9::BytesPerPixel(u32 format) {
return format == D3DFMT_A8R8G8B8 ? 4 : 2;
}
u32 TextureScalerDX9::Get8888Format() {
return D3DFMT_A8R8G8B8;
}
void TextureScalerDX9::ConvertTo8888(u32 format, u32* source, u32* &dest, int width, int height) {
switch(format) {
case D3DFMT_A8R8G8B8:
dest = source; // already fine
break;
case D3DFMT_A4R4G4B4:
ParallelRangeLoop(&g_threadManager, std::bind(&convert4444_dx9, (u16*)source, dest, width, std::placeholders::_1, std::placeholders::_2), 0, height, MIN_TEXSCALE_LINES_PER_THREAD);
break;
case D3DFMT_R5G6B5:
ParallelRangeLoop(&g_threadManager, std::bind(&convert565_dx9, (u16*)source, dest, width, std::placeholders::_1, std::placeholders::_2), 0, height, MIN_TEXSCALE_LINES_PER_THREAD);
break;
case D3DFMT_A1R5G5B5:
ParallelRangeLoop(&g_threadManager, std::bind(&convert5551_dx9, (u16*)source, dest, width, std::placeholders::_1, std::placeholders::_2), 0, height, MIN_TEXSCALE_LINES_PER_THREAD);
break;
default:
dest = source;
ERROR_LOG(G3D, "iXBRZTexScaling: unsupported texture format");
}
}
} // namespace

View File

@ -1,32 +0,0 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include "Common/CommonTypes.h"
#include "GPU/Common/TextureScalerCommon.h"
namespace DX9 {
class TextureScalerDX9 : public TextureScalerCommon {
private:
void ConvertTo8888(u32 format, u32* source, u32* &dest, int width, int height) override;
int BytesPerPixel(u32 format) override;
u32 Get8888Format() override;
};
};

View File

@ -228,7 +228,7 @@ void TextureCacheGLES::BindTexture(TexCacheEntry *entry) {
render_->BindTexture(0, entry->textureName);
lastBoundTexture = entry->textureName;
}
int maxLevel = (entry->status & TexCacheEntry::STATUS_BAD_MIPS) ? 0 : entry->maxLevel;
int maxLevel = (entry->status & TexCacheEntry::STATUS_NO_MIPS) ? 0 : entry->maxLevel;
SamplerCacheKey samplerKey = GetSamplingParams(maxLevel, entry);
ApplySamplingParams(samplerKey);
gstate_c.SetUseShaderDepal(false);
@ -425,170 +425,92 @@ void TextureCacheGLES::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer,
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE);
}
ReplacedTextureFormat FromDataFormat(Draw::DataFormat fmt) {
// TODO: 16-bit formats are incorrect, since swizzled.
switch (fmt) {
case Draw::DataFormat::R5G6B5_UNORM_PACK16: return ReplacedTextureFormat::F_0565_ABGR;
case Draw::DataFormat::R5G5B5A1_UNORM_PACK16: return ReplacedTextureFormat::F_1555_ABGR;
case Draw::DataFormat::R4G4B4A4_UNORM_PACK16: return ReplacedTextureFormat::F_4444_ABGR;
case Draw::DataFormat::R8G8B8A8_UNORM: default: return ReplacedTextureFormat::F_8888;
}
}
Draw::DataFormat ToDataFormat(ReplacedTextureFormat fmt) {
switch (fmt) {
case ReplacedTextureFormat::F_5650: return Draw::DataFormat::R5G6B5_UNORM_PACK16;
case ReplacedTextureFormat::F_5551: return Draw::DataFormat::R5G5B5A1_UNORM_PACK16;
case ReplacedTextureFormat::F_4444: return Draw::DataFormat::R4G4B4A4_UNORM_PACK16;
case ReplacedTextureFormat::F_8888: default: return Draw::DataFormat::R8G8B8A8_UNORM;
}
}
void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) {
entry->status &= ~TexCacheEntry::STATUS_ALPHA_MASK;
// For the estimate, we assume cluts always point to 8888 for simplicity.
cacheSizeEstimate_ += EstimateTexMemoryUsage(entry);
if ((entry->bufw == 0 || (gstate.texbufwidth[0] & 0xf800) != 0) && entry->addr >= PSP_GetKernelMemoryEnd()) {
ERROR_LOG_REPORT(G3D, "Texture with unexpected bufw (full=%d)", gstate.texbufwidth[0] & 0xffff);
// Proceeding here can cause a crash.
BuildTexturePlan plan;
if (!PrepareBuildTexture(plan, entry)) {
// We're screwed?
return;
}
// Adjust maxLevel to actually present levels..
bool badMipSizes = false;
bool canAutoGen = false;
int maxLevel = entry->maxLevel;
for (int i = 0; i <= maxLevel; i++) {
// If encountering levels pointing to nothing, adjust max level.
u32 levelTexaddr = gstate.getTextureAddress(i);
if (!Memory::IsValidAddress(levelTexaddr)) {
maxLevel = i - 1;
break;
}
// If size reaches 1, stop, and override maxlevel.
int tw = gstate.getTextureWidth(i);
int th = gstate.getTextureHeight(i);
if (tw == 1 || th == 1) {
maxLevel = i;
break;
}
if (i > 0) {
int lastW = gstate.getTextureWidth(i - 1);
int lastH = gstate.getTextureHeight(i - 1);
if (gstate_c.Supports(GPU_SUPPORTS_TEXTURE_LOD_CONTROL)) {
if (tw != 1 && tw != (lastW >> 1))
badMipSizes = true;
else if (th != 1 && th != (lastH >> 1))
badMipSizes = true;
}
if (lastW > tw || lastH > th)
canAutoGen = true;
}
}
// If GLES3 is available, we can preallocate the storage, which makes texture loading more efficient.
Draw::DataFormat dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat());
int scaleFactor = standardScaleFactor_;
// Rachet down scale factor in low-memory mode.
if (lowMemoryMode_) {
// Keep it even, though, just in case of npot troubles.
scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1);
}
int w = gstate.getTextureWidth(0);
int h = gstate.getTextureHeight(0);
ReplacedTexture &replaced = FindReplacement(entry, w, h);
if (replaced.Valid()) {
// We're replacing, so we won't scale.
scaleFactor = 1;
maxLevel = replaced.MaxLevel();
badMipSizes = false;
}
// Don't scale the PPGe texture.
if (entry->addr > 0x05000000 && entry->addr < PSP_GetKernelMemoryEnd())
scaleFactor = 1;
if ((entry->status & TexCacheEntry::STATUS_CHANGE_FREQUENT) != 0 && scaleFactor != 1) {
// Remember for later that we /wanted/ to scale this texture.
entry->status |= TexCacheEntry::STATUS_TO_SCALE;
scaleFactor = 1;
}
if (scaleFactor != 1) {
if (texelsScaledThisFrame_ >= TEXCACHE_MAX_TEXELS_SCALED) {
entry->status |= TexCacheEntry::STATUS_TO_SCALE;
scaleFactor = 1;
} else {
entry->status &= ~TexCacheEntry::STATUS_TO_SCALE;
entry->status |= TexCacheEntry::STATUS_IS_SCALED;
texelsScaledThisFrame_ += w * h;
}
}
_assert_(!entry->textureName);
// GLES2 doesn't have support for a "Max lod" which is critical as PSP games often
// don't specify mips all the way down. As a result, we either need to manually generate
// the bottom few levels or rely on OpenGL's autogen mipmaps instead, which might not
// be as good quality as the game's own (might even be better in some cases though).
// Always load base level texture here
u8 level = 0;
if (IsFakeMipmapChange()) {
// NOTE: Since the level is not part of the cache key, we assume it never changes.
level = std::max(0, gstate.getTexLevelOffset16() / 16);
}
LoadTextureLevel(*entry, replaced, level, scaleFactor, dstFmt);
int tw = plan.w;
int th = plan.h;
// Mipmapping is only enabled when texture scaling is disabled.
int texMaxLevel = 0;
bool genMips = false;
if (maxLevel > 0 && scaleFactor == 1) {
if (gstate_c.Supports(GPU_SUPPORTS_TEXTURE_LOD_CONTROL)) {
if (badMipSizes) {
// WARN_LOG(G3D, "Bad mipmap for texture sized %dx%dx%d - autogenerating", w, h, (int)format);
if (canAutoGen) {
genMips = true;
} else {
texMaxLevel = 0;
maxLevel = 0;
}
} else {
for (int i = 1; i <= maxLevel; i++) {
LoadTextureLevel(*entry, replaced, i, scaleFactor, dstFmt);
}
texMaxLevel = maxLevel;
}
Draw::DataFormat dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat());
if (plan.replaced->GetSize(plan.baseLevelSrc, tw, th)) {
dstFmt = plan.replaced->Format(plan.baseLevelSrc);
} else if (plan.scaleFactor > 1) {
tw *= plan.scaleFactor;
th *= plan.scaleFactor;
dstFmt = Draw::DataFormat::R8G8B8A8_UNORM;
}
entry->textureName = render_->CreateTexture(GL_TEXTURE_2D, tw, tw, plan.levelsToCreate);
// Apply some additional compatibility checks.
if (plan.levelsToLoad > 1) {
// Avoid PowerVR driver bug
if (plan.w > 1 && plan.h > 1 && !(plan.h > plan.w && draw_->GetBugs().Has(Draw::Bugs::PVR_GENMIPMAP_HEIGHT_GREATER))) { // Really! only seems to fail if height > width
// It's ok to generate mipmaps beyond the loaded levels.
} else {
// Avoid PowerVR driver bug
if (canAutoGen && w > 1 && h > 1 && !(h > w && draw_->GetBugs().Has(Draw::Bugs::PVR_GENMIPMAP_HEIGHT_GREATER))) { // Really! only seems to fail if height > width
// NOTICE_LOG(G3D, "Generating mipmap for texture sized %dx%d%d", w, h, (int)format);
genMips = true;
plan.levelsToCreate = plan.levelsToLoad;
}
}
if (!gstate_c.Supports(GPU_SUPPORTS_TEXTURE_LOD_CONTROL)) {
// Force no additional mipmaps.
plan.levelsToCreate = plan.levelsToLoad;
}
for (int i = 0; i < plan.levelsToLoad; i++) {
int srcLevel = i == 0 ? plan.baseLevelSrc : i;
int w = gstate.getTextureWidth(srcLevel);
int h = gstate.getTextureHeight(srcLevel);
u8 *data = nullptr;
int stride = 0;
if (plan.replaced->GetSize(srcLevel, w, h)) {
int bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel));
stride = w * bpp;
data = (u8 *)AllocateAlignedMemory(stride * h, 16);
} else {
if (plan.scaleFactor > 1) {
data = (u8 *)AllocateAlignedMemory(4 * (w * plan.scaleFactor) * (h * plan.scaleFactor), 16);
stride = w * plan.scaleFactor * 4;
} else {
maxLevel = 0;
int bpp = dstFmt == Draw::DataFormat::R8G8B8A8_UNORM ? 4 : 2;
stride = std::max(w * bpp, 4);
data = (u8 *)AllocateAlignedMemory(stride * h, 16);
}
}
} else if (gstate_c.Supports(GPU_SUPPORTS_TEXTURE_LOD_CONTROL)) {
texMaxLevel = 0;
if (!data) {
ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", w, h);
return;
}
LoadTextureLevel(*entry, data, stride, *plan.replaced, srcLevel, plan.scaleFactor, dstFmt, true);
// NOTE: TextureImage takes ownership of data, so we don't free it afterwards.
render_->TextureImage(entry->textureName, i, w * plan.scaleFactor, h * plan.scaleFactor, dstFmt, data, GLRAllocType::ALIGNED);
}
if (maxLevel == 0) {
entry->status |= TexCacheEntry::STATUS_BAD_MIPS;
} else {
entry->status &= ~TexCacheEntry::STATUS_BAD_MIPS;
}
if (replaced.Valid()) {
entry->SetAlphaStatus(TexCacheEntry::TexStatus(replaced.AlphaStatus()));
}
bool genMips = plan.levelsToCreate > plan.levelsToLoad;
render_->FinalizeTexture(entry->textureName, texMaxLevel, genMips);
render_->FinalizeTexture(entry->textureName, plan.levelsToLoad, genMips);
if (plan.replaced->Valid()) {
entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus()));
}
}
Draw::DataFormat TextureCacheGLES::GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const {
@ -627,81 +549,6 @@ CheckAlphaResult TextureCacheGLES::CheckAlpha(const uint8_t *pixelData, Draw::Da
}
}
void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &replaced, int level, int scaleFactor, Draw::DataFormat dstFmt) {
int w = gstate.getTextureWidth(level);
int h = gstate.getTextureHeight(level);
uint8_t *pixelData;
int decPitch = 0;
gpuStats.numTexturesDecoded++;
if (!entry.textureName) {
// TODO: Actually pass in correct size here. The size here is not yet used for anything else
// than determining if we can wrap this texture size, that is, it's pow2 or not on very old hardware, else true.
// This will be easy after .. well, yet another refactoring, where I hoist the size calculation out of LoadTextureLevel
// and unify BuildTexture.
entry.textureName = render_->CreateTexture(GL_TEXTURE_2D, 16, 16, 1);
}
if (replaced.GetSize(level, w, h)) {
PROFILE_THIS_SCOPE("replacetex");
int bpp = replaced.Format(level) == ReplacedTextureFormat::F_8888 ? 4 : 2;
decPitch = w * bpp;
uint8_t *rearrange = (uint8_t *)AllocateAlignedMemory(decPitch * h, 16);
double replaceStart = time_now_d();
replaced.Load(level, rearrange, decPitch);
replacementTimeThisFrame_ += time_now_d() - replaceStart;
pixelData = rearrange;
dstFmt = ToDataFormat(replaced.Format(level));
} else {
PROFILE_THIS_SCOPE("decodetex");
GEPaletteFormat clutformat = gstate.getClutPaletteFormat();
u32 texaddr = gstate.getTextureAddress(level);
int bufw = GetTextureBufw(level, texaddr, GETextureFormat(entry.format));
int pixelSize = dstFmt == Draw::DataFormat::R8G8B8A8_UNORM ? 4 : 2;
// We leave GL_UNPACK_ALIGNMENT at 4, so this must be at least 4.
decPitch = std::max(w * pixelSize, 4);
pixelData = (uint8_t *)AllocateAlignedMemory(decPitch * h * pixelSize, 16);
CheckAlphaResult alphaStatus = DecodeTextureLevel(pixelData, decPitch, GETextureFormat(entry.format), clutformat, texaddr, level, bufw, true, false, false);
entry.SetAlphaStatus(alphaStatus, level);
if (scaleFactor > 1) {
uint8_t *rearrange = (uint8_t *)AllocateAlignedMemory(w * scaleFactor * h * scaleFactor * 4, 16);
u32 dFmt = (u32)dstFmt;
scaler.ScaleAlways((u32 *)rearrange, (u32 *)pixelData, dFmt, w, h, scaleFactor);
dstFmt = (Draw::DataFormat)dFmt;
FreeAlignedMemory(pixelData);
pixelData = rearrange;
decPitch = w * 4;
}
if (replacer_.Enabled()) {
ReplacedTextureDecodeInfo replacedInfo;
replacedInfo.cachekey = entry.CacheKey();
replacedInfo.hash = entry.fullhash;
replacedInfo.addr = entry.addr;
replacedInfo.isVideo = IsVideo(entry.addr);
replacedInfo.isFinal = (entry.status & TexCacheEntry::STATUS_TO_SCALE) == 0;
replacedInfo.scaleFactor = scaleFactor;
replacedInfo.fmt = FromDataFormat(dstFmt);
replacer_.NotifyTextureDecoded(replacedInfo, pixelData, decPitch, level, w, h);
}
}
PROFILE_THIS_SCOPE("loadtex");
if (IsFakeMipmapChange())
render_->TextureImage(entry.textureName, 0, w, h, dstFmt, pixelData, GLRAllocType::ALIGNED);
else
render_->TextureImage(entry.textureName, level, w, h, dstFmt, pixelData, GLRAllocType::ALIGNED);
}
bool TextureCacheGLES::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) {
GPUgstate saved;
if (level != 0) {

View File

@ -22,7 +22,6 @@
#include "Common/GPU/OpenGL/GLRenderManager.h"
#include "GPU/GPUInterface.h"
#include "GPU/GPUState.h"
#include "GPU/GLES/TextureScalerGLES.h"
#include "GPU/Common/TextureCacheCommon.h"
struct VirtualFramebuffer;
@ -70,7 +69,6 @@ protected:
private:
void ApplySamplingParams(const SamplerCacheKey &key);
void LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &replaced, int level, int scaleFactor, Draw::DataFormat dstFmt);
Draw::DataFormat GetDestFormat(GETextureFormat format, GEPaletteFormat clutFormat) const;
static CheckAlphaResult CheckAlpha(const uint8_t *pixelData, Draw::DataFormat dstFmt, int w);
@ -81,8 +79,6 @@ private:
GLRenderManager *render_;
TextureScalerGLES scaler;
GLRTexture *lastBoundTexture = nullptr;
FramebufferManagerGLES *framebufferManagerGL_;

View File

@ -1,61 +0,0 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <algorithm>
#include "Common/Data/Convert/ColorConv.h"
#include "Common/Log.h"
#include "Common/Thread/ParallelLoop.h"
#include "Common/GPU/OpenGL/GLCommon.h"
#include "Common/GPU/DataFormat.h"
#include "Core/ThreadPools.h"
#include "GPU/Common/TextureScalerCommon.h"
#include "GPU/GLES/TextureScalerGLES.h"
int TextureScalerGLES::BytesPerPixel(u32 format) {
return ((Draw::DataFormat)format == Draw::DataFormat::R8G8B8A8_UNORM) ? 4 : 2;
}
u32 TextureScalerGLES::Get8888Format() {
return (u32)Draw::DataFormat::R8G8B8A8_UNORM;
}
void TextureScalerGLES::ConvertTo8888(u32 format, u32* source, u32* &dest, int width, int height) {
Draw::DataFormat fmt = (Draw::DataFormat)format;
switch (fmt) {
case Draw::DataFormat::R8G8B8A8_UNORM:
dest = source; // already fine
break;
case Draw::DataFormat::R4G4B4A4_UNORM_PACK16:
ParallelRangeLoop(&g_threadManager, std::bind(&convert4444_gl, (u16*)source, dest, width, std::placeholders::_1, std::placeholders::_2), 0, height, MIN_TEXSCALE_LINES_PER_THREAD);
break;
case Draw::DataFormat::R5G6B5_UNORM_PACK16:
ParallelRangeLoop(&g_threadManager, std::bind(&convert565_gl, (u16*)source, dest, width, std::placeholders::_1, std::placeholders::_2), 0, height, MIN_TEXSCALE_LINES_PER_THREAD);
break;
case Draw::DataFormat::R5G5B5A1_UNORM_PACK16:
ParallelRangeLoop(&g_threadManager, std::bind(&convert5551_gl, (u16*)source, dest, width, std::placeholders::_1, std::placeholders::_2), 0, height, MIN_TEXSCALE_LINES_PER_THREAD);
break;
default:
dest = source;
ERROR_LOG(G3D, "iXBRZTexScaling: unsupported texture format");
}
}

View File

@ -1,27 +0,0 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include "Common/CommonTypes.h"
#include "GPU/Common/TextureScalerCommon.h"
class TextureScalerGLES : public TextureScalerCommon {
void ConvertTo8888(u32 format, u32* source, u32* &dest, int width, int height) override;
int BytesPerPixel(u32 format) override;
u32 Get8888Format() override;
};

View File

@ -367,7 +367,6 @@
<ClInclude Include="D3D11\ShaderManagerD3D11.h" />
<ClInclude Include="D3D11\StateMappingD3D11.h" />
<ClInclude Include="D3D11\TextureCacheD3D11.h" />
<ClInclude Include="D3D11\TextureScalerD3D11.h" />
<ClInclude Include="Debugger\Breakpoints.h" />
<ClInclude Include="Debugger\Debugger.h" />
<ClInclude Include="Debugger\Playback.h" />
@ -379,7 +378,6 @@
<ClInclude Include="Directx9\GPU_DX9.h" />
<ClInclude Include="Directx9\ShaderManagerDX9.h" />
<ClInclude Include="Directx9\TextureCacheDX9.h" />
<ClInclude Include="Directx9\TextureScalerDX9.h" />
<ClInclude Include="Directx9\DrawEngineDX9.h" />
<ClInclude Include="ge_constants.h" />
<ClInclude Include="GeDisasm.h" />
@ -425,12 +423,6 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="GLES\TextureScalerGLES.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="GLES\DrawEngineGLES.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
@ -463,7 +455,6 @@
<ClInclude Include="Vulkan\ShaderManagerVulkan.h" />
<ClInclude Include="Vulkan\StateMappingVulkan.h" />
<ClInclude Include="Vulkan\TextureCacheVulkan.h" />
<ClInclude Include="Vulkan\TextureScalerVulkan.h" />
<ClInclude Include="Vulkan\VulkanUtil.h" />
</ItemGroup>
<ItemGroup>
@ -524,7 +515,6 @@
<ClCompile Include="D3D11\StateMappingD3D11.cpp" />
<ClCompile Include="D3D11\StencilBufferD3D11.cpp" />
<ClCompile Include="D3D11\TextureCacheD3D11.cpp" />
<ClCompile Include="D3D11\TextureScalerD3D11.cpp" />
<ClCompile Include="Debugger\Breakpoints.cpp" />
<ClCompile Include="Debugger\Debugger.cpp" />
<ClCompile Include="Debugger\Playback.cpp" />
@ -537,7 +527,6 @@
<ClCompile Include="Directx9\StateMappingDX9.cpp" />
<ClCompile Include="Directx9\StencilBufferDX9.cpp" />
<ClCompile Include="Directx9\TextureCacheDX9.cpp" />
<ClCompile Include="Directx9\TextureScalerDX9.cpp" />
<ClCompile Include="Directx9\DrawEngineDX9.cpp" />
<ClCompile Include="GeDisasm.cpp" />
<ClCompile Include="GeConstants.cpp" />
@ -595,12 +584,6 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GLES\TextureScalerGLES.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GLES\DrawEngineGLES.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
@ -635,7 +618,6 @@
<ClCompile Include="Vulkan\StateMappingVulkan.cpp" />
<ClCompile Include="Vulkan\StencilBufferVulkan.cpp" />
<ClCompile Include="Vulkan\TextureCacheVulkan.cpp" />
<ClCompile Include="Vulkan\TextureScalerVulkan.cpp" />
<ClCompile Include="Vulkan\VulkanUtil.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -66,9 +66,6 @@
<ClInclude Include="Directx9\DrawEngineDX9.h">
<Filter>DirectX9</Filter>
</ClInclude>
<ClInclude Include="Directx9\TextureScalerDX9.h">
<Filter>DirectX9</Filter>
</ClInclude>
<ClInclude Include="Directx9\TextureCacheDX9.h">
<Filter>DirectX9</Filter>
</ClInclude>
@ -162,9 +159,6 @@
<ClInclude Include="Vulkan\TextureCacheVulkan.h">
<Filter>Vulkan</Filter>
</ClInclude>
<ClInclude Include="Vulkan\TextureScalerVulkan.h">
<Filter>Vulkan</Filter>
</ClInclude>
<ClInclude Include="Vulkan\VulkanUtil.h">
<Filter>Vulkan</Filter>
</ClInclude>
@ -177,9 +171,6 @@
<ClInclude Include="GLES\StateMappingGLES.h">
<Filter>GLES</Filter>
</ClInclude>
<ClInclude Include="GLES\TextureScalerGLES.h">
<Filter>GLES</Filter>
</ClInclude>
<ClInclude Include="GLES\DepalettizeShaderGLES.h">
<Filter>GLES</Filter>
</ClInclude>
@ -210,9 +201,6 @@
<ClInclude Include="D3D11\DepalettizeShaderD3D11.h">
<Filter>D3D11</Filter>
</ClInclude>
<ClInclude Include="D3D11\TextureScalerD3D11.h">
<Filter>D3D11</Filter>
</ClInclude>
<ClInclude Include="D3D11\D3D11Util.h">
<Filter>D3D11</Filter>
</ClInclude>
@ -323,9 +311,6 @@
<ClCompile Include="Directx9\ShaderManagerDX9.cpp">
<Filter>DirectX9</Filter>
</ClCompile>
<ClCompile Include="Directx9\TextureScalerDX9.cpp">
<Filter>DirectX9</Filter>
</ClCompile>
<ClCompile Include="Common\IndexGenerator.cpp">
<Filter>Common</Filter>
</ClCompile>
@ -419,9 +404,6 @@
<ClCompile Include="Vulkan\TextureCacheVulkan.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
<ClCompile Include="Vulkan\TextureScalerVulkan.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
<ClCompile Include="Vulkan\VulkanUtil.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
@ -434,9 +416,6 @@
<ClCompile Include="GLES\StateMappingGLES.cpp">
<Filter>GLES</Filter>
</ClCompile>
<ClCompile Include="GLES\TextureScalerGLES.cpp">
<Filter>GLES</Filter>
</ClCompile>
<ClCompile Include="GLES\StencilBufferGLES.cpp">
<Filter>GLES</Filter>
</ClCompile>
@ -473,9 +452,6 @@
<ClCompile Include="D3D11\DepalettizeShaderD3D11.cpp">
<Filter>D3D11</Filter>
</ClCompile>
<ClCompile Include="D3D11\TextureScalerD3D11.cpp">
<Filter>D3D11</Filter>
</ClCompile>
<ClCompile Include="D3D11\D3D11Util.cpp">
<Filter>D3D11</Filter>
</ClCompile>

View File

@ -2649,6 +2649,7 @@ void GPUCommon::ResetListPC(int listID, u32 pc) {
return;
}
Reporting::NotifyDebugger();
dls[listID].pc = pc;
downcount = 0;
}
@ -2659,6 +2660,7 @@ void GPUCommon::ResetListStall(int listID, u32 stall) {
return;
}
Reporting::NotifyDebugger();
dls[listID].stall = stall;
downcount = 0;
}
@ -2669,6 +2671,7 @@ void GPUCommon::ResetListState(int listID, DisplayListState state) {
return;
}
Reporting::NotifyDebugger();
dls[listID].state = state;
downcount = 0;
}
@ -2726,6 +2729,7 @@ void GPUCommon::SetCmdValue(u32 op) {
u32 cmd = op >> 24;
u32 diff = op ^ gstate.cmdmem[cmd];
Reporting::NotifyDebugger();
PreExecuteOp(op, diff);
gstate.cmdmem[cmd] = op;
ExecuteOp(op, diff);

View File

@ -392,7 +392,7 @@ void TextureCacheVulkan::BindTexture(TexCacheEntry *entry) {
entry->vkTex->Touch();
imageView_ = entry->vkTex->GetImageView();
int maxLevel = (entry->status & TexCacheEntry::STATUS_BAD_MIPS) ? 0 : entry->maxLevel;
int maxLevel = (entry->status & TexCacheEntry::STATUS_NO_MIPS) ? 0 : entry->maxLevel;
SamplerCacheKey samplerKey = GetSamplingParams(maxLevel, entry);
curSampler_ = samplerCache_.GetOrCreateSampler(samplerKey);
drawEngine_->SetDepalTexture(VK_NULL_HANDLE);
@ -564,375 +564,241 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
curSampler_ = samplerCache_.GetOrCreateSampler(samplerKey);
}
ReplacedTextureFormat FromVulkanFormat(VkFormat fmt) {
static Draw::DataFormat FromVulkanFormat(VkFormat fmt) {
switch (fmt) {
case VULKAN_565_FORMAT: return ReplacedTextureFormat::F_5650;
case VULKAN_1555_FORMAT: return ReplacedTextureFormat::F_5551;
case VULKAN_4444_FORMAT: return ReplacedTextureFormat::F_4444;
case VULKAN_8888_FORMAT: default: return ReplacedTextureFormat::F_8888;
case VULKAN_8888_FORMAT: default: return Draw::DataFormat::R8G8B8A8_UNORM;
}
}
VkFormat ToVulkanFormat(ReplacedTextureFormat fmt) {
static VkFormat ToVulkanFormat(Draw::DataFormat fmt) {
switch (fmt) {
case ReplacedTextureFormat::F_5650: return VULKAN_565_FORMAT;
case ReplacedTextureFormat::F_5551: return VULKAN_1555_FORMAT;
case ReplacedTextureFormat::F_4444: return VULKAN_4444_FORMAT;
case ReplacedTextureFormat::F_8888: default: return VULKAN_8888_FORMAT;
case Draw::DataFormat::R8G8B8A8_UNORM: default: return VULKAN_8888_FORMAT;
}
}
void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
entry->status &= ~TexCacheEntry::STATUS_ALPHA_MASK;
VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT);
// For the estimate, we assume cluts always point to 8888 for simplicity.
cacheSizeEstimate_ += EstimateTexMemoryUsage(entry);
if ((entry->bufw == 0 || (gstate.texbufwidth[0] & 0xf800) != 0) && entry->addr >= PSP_GetKernelMemoryEnd()) {
ERROR_LOG_REPORT(G3D, "Texture with unexpected bufw (full=%d)", gstate.texbufwidth[0] & 0xffff);
// Proceeding here can cause a crash.
BuildTexturePlan plan;
plan.hardwareScaling = g_Config.bTexHardwareScaling && uploadCS_ != VK_NULL_HANDLE;
plan.slowScaler = !plan.hardwareScaling || vulkan->DevicePerfClass() == PerfClass::SLOW;
if (!PrepareBuildTexture(plan, entry)) {
// We're screwed?
return;
}
VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT);
// Adjust maxLevel to actually present levels..
bool badMipSizes = false;
// maxLevel here is the max level to upload. Not the count.
int maxLevel = entry->maxLevel;
for (int i = 0; i <= maxLevel; i++) {
// If encountering levels pointing to nothing, adjust max level.
u32 levelTexaddr = gstate.getTextureAddress(i);
if (!Memory::IsValidAddress(levelTexaddr)) {
maxLevel = i - 1;
break;
}
// If size reaches 1, stop, and override maxlevel.
int tw = gstate.getTextureWidth(i);
int th = gstate.getTextureHeight(i);
if (tw == 1 || th == 1) {
maxLevel = i;
break;
}
if (i > 0 && gstate_c.Supports(GPU_SUPPORTS_TEXTURE_LOD_CONTROL)) {
if (tw != 1 && tw != (gstate.getTextureWidth(i - 1) >> 1))
badMipSizes = true;
else if (th != 1 && th != (gstate.getTextureHeight(i - 1) >> 1))
badMipSizes = true;
}
int maxPossibleMipLevels;
if (plan.isVideo) {
maxPossibleMipLevels = 1;
} else {
maxPossibleMipLevels = log2i(std::min(plan.w * plan.scaleFactor, plan.h * plan.scaleFactor)) + 1;
}
// In addition, simply don't load more than level 0 if g_Config.bMipMap is false.
if (badMipSizes) {
maxLevel = 0;
}
// We generate missing mipmaps from maxLevel+1 up to this level. maxLevel can get overwritten below
// such as when using replacement textures - but let's keep the same amount of levels.
int maxLevelToGenerate = maxLevel;
VkFormat dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat());
int scaleFactor = standardScaleFactor_;
bool hardwareScaling = g_Config.bTexHardwareScaling && uploadCS_ != VK_NULL_HANDLE;
if (hardwareScaling) {
scaleFactor = shaderScaleFactor_;
if (plan.hardwareScaling) {
dstFmt = VK_FORMAT_R8G8B8A8_UNORM;
}
// Rachet down scale factor in low-memory mode.
// TODO: I think really we should just turn it off?
if (lowMemoryMode_ && !hardwareScaling) {
// Keep it even, though, just in case of npot troubles.
scaleFactor = scaleFactor > 4 ? 4 : (scaleFactor > 2 ? 2 : 1);
}
int w = gstate.getTextureWidth(0);
int h = gstate.getTextureHeight(0);
ReplacedTexture &replaced = FindReplacement(entry, w, h);
if (replaced.Valid()) {
// We're replacing, so we won't scale.
scaleFactor = 1;
maxLevel = replaced.MaxLevel();
badMipSizes = false;
}
// Don't scale the PPGe texture.
if (entry->addr > 0x05000000 && entry->addr < PSP_GetKernelMemoryEnd()) {
scaleFactor = 1;
}
bool slowScaler = !hardwareScaling || vulkan->DevicePerfClass() == PerfClass::SLOW; // Or the GPU is slow - TODO add check!
if ((entry->status & TexCacheEntry::STATUS_CHANGE_FREQUENT) != 0 && scaleFactor != 1 && slowScaler) {
// Remember for later that we /wanted/ to scale this texture.
entry->status |= TexCacheEntry::STATUS_TO_SCALE;
scaleFactor = 1;
}
if (scaleFactor != 1) {
if (texelsScaledThisFrame_ >= TEXCACHE_MAX_TEXELS_SCALED && slowScaler) {
entry->status |= TexCacheEntry::STATUS_TO_SCALE;
scaleFactor = 1;
} else {
entry->status &= ~TexCacheEntry::STATUS_TO_SCALE;
entry->status |= TexCacheEntry::STATUS_IS_SCALED;
texelsScaledThisFrame_ += w * h;
}
}
bool isVideo = IsVideo(entry->addr);
// TODO: Support reading actual mip levels for upscaled images, instead of just generating them.
// Probably can just remove this check?
if (scaleFactor > 1) {
maxLevel = 0;
bool enableVideoUpscaling = false;
if (!enableVideoUpscaling && isVideo) {
scaleFactor = 1;
}
}
int maxPossibleMipLevel = log2i(std::min(w * scaleFactor, h * scaleFactor));
if (maxPossibleMipLevel > 0 && isVideo) {
maxPossibleMipLevel = 0;
}
// We don't generate mipmaps for 512x512 textures because they're almost exclusively used for menu backgrounds
// and similar, which don't really need it.
if (g_Config.iTexFiltering == TEX_FILTER_AUTO_MAX_QUALITY && w <= 256 && h <= 256) {
if (g_Config.iTexFiltering == TEX_FILTER_AUTO_MAX_QUALITY && plan.w <= 256 && plan.h <= 256) {
// Boost the number of mipmaps.
if (maxPossibleMipLevel > maxLevelToGenerate) {
if (maxPossibleMipLevels > plan.levelsToCreate) {
// We have to generate mips with a shader. This requires decoding to R8G8B8A8_UNORM format to avoid extra complications.
dstFmt = VK_FORMAT_R8G8B8A8_UNORM;
}
maxLevelToGenerate = maxPossibleMipLevel;
plan.levelsToCreate = maxPossibleMipLevels;
}
// Any texture scaling is gonna move away from the original 16-bit format, if any.
VkFormat actualFmt = scaleFactor > 1 ? VULKAN_8888_FORMAT : dstFmt;
if (replaced.Valid()) {
actualFmt = ToVulkanFormat(replaced.Format(0));
VkFormat actualFmt = plan.scaleFactor > 1 ? VULKAN_8888_FORMAT : dstFmt;
if (plan.replaced->Valid()) {
actualFmt = ToVulkanFormat(plan.replaced->Format(0));
}
bool computeUpload = false;
VkCommandBuffer cmdInit = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER);
{
delete entry->vkTex;
entry->vkTex = new VulkanTexture(vulkan);
VulkanTexture *image = entry->vkTex;
const VkComponentMapping *mapping;
switch (actualFmt) {
case VULKAN_4444_FORMAT: mapping = &VULKAN_4444_SWIZZLE; break;
case VULKAN_1555_FORMAT: mapping = &VULKAN_1555_SWIZZLE; break;
case VULKAN_565_FORMAT: mapping = &VULKAN_565_SWIZZLE; break;
default: mapping = &VULKAN_8888_SWIZZLE; break;
}
VkImageLayout imageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
if (actualFmt == VULKAN_8888_FORMAT && plan.scaleFactor > 1 && plan.hardwareScaling) {
if (uploadCS_ != VK_NULL_HANDLE) {
computeUpload = true;
} else {
WARN_LOG(G3D, "Falling back to software scaling, hardware shader didn't compile");
}
}
if (computeUpload) {
usage |= VK_IMAGE_USAGE_STORAGE_BIT;
imageLayout = VK_IMAGE_LAYOUT_GENERAL;
}
char texName[128]{};
snprintf(texName, sizeof(texName), "tex_%08x_%s", entry->addr, GeTextureFormatToString((GETextureFormat)entry->format, gstate.getClutPaletteFormat()));
image->SetTag(texName);
bool allocSuccess = image->CreateDirect(cmdInit, plan.w * plan.scaleFactor, plan.h * plan.scaleFactor, plan.levelsToCreate, actualFmt, imageLayout, usage, mapping);
if (!allocSuccess && !lowMemoryMode_) {
WARN_LOG_REPORT(G3D, "Texture cache ran out of GPU memory; switching to low memory mode");
lowMemoryMode_ = true;
decimationCounter_ = 0;
Decimate();
// TODO: We should stall the GPU here and wipe things out of memory.
// As is, it will almost definitely fail the second time, but next frame it may recover.
auto err = GetI18NCategory("Error");
if (plan.scaleFactor > 1) {
host->NotifyUserMessage(err->T("Warning: Video memory FULL, reducing upscaling and switching to slow caching mode"), 2.0f);
} else {
host->NotifyUserMessage(err->T("Warning: Video memory FULL, switching to slow caching mode"), 2.0f);
}
plan.scaleFactor = 1;
actualFmt = dstFmt;
allocSuccess = image->CreateDirect(cmdInit, plan.w * plan.scaleFactor, plan.h * plan.scaleFactor, plan.levelsToCreate, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping);
}
if (!allocSuccess) {
ERROR_LOG(G3D, "Failed to create texture (%dx%d)", plan.w, plan.h);
delete entry->vkTex;
entry->vkTex = new VulkanTexture(vulkan);
VulkanTexture *image = entry->vkTex;
entry->vkTex = nullptr;
}
const VkComponentMapping *mapping;
switch (actualFmt) {
case VULKAN_4444_FORMAT:
mapping = &VULKAN_4444_SWIZZLE;
break;
case VULKAN_1555_FORMAT:
mapping = &VULKAN_1555_SWIZZLE;
break;
case VULKAN_565_FORMAT:
mapping = &VULKAN_565_SWIZZLE;
break;
default:
mapping = &VULKAN_8888_SWIZZLE;
break;
}
VkImageLayout imageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
if (actualFmt == VULKAN_8888_FORMAT && scaleFactor > 1 && hardwareScaling) {
if (uploadCS_ != VK_NULL_HANDLE) {
computeUpload = true;
} else {
WARN_LOG(G3D, "Falling back to software scaling, hardware shader didn't compile");
}
}
if (computeUpload) {
usage |= VK_IMAGE_USAGE_STORAGE_BIT;
imageLayout = VK_IMAGE_LAYOUT_GENERAL;
}
char texName[128]{};
snprintf(texName, sizeof(texName), "tex_%08x_%s", entry->addr, GeTextureFormatToString((GETextureFormat)entry->format, gstate.getClutPaletteFormat()));
image->SetTag(texName);
bool allocSuccess = image->CreateDirect(cmdInit, w * scaleFactor, h * scaleFactor, maxLevelToGenerate + 1, actualFmt, imageLayout, usage, mapping);
if (!allocSuccess && !lowMemoryMode_) {
WARN_LOG_REPORT(G3D, "Texture cache ran out of GPU memory; switching to low memory mode");
lowMemoryMode_ = true;
decimationCounter_ = 0;
Decimate();
// TODO: We should stall the GPU here and wipe things out of memory.
// As is, it will almost definitely fail the second time, but next frame it may recover.
auto err = GetI18NCategory("Error");
if (scaleFactor > 1) {
host->NotifyUserMessage(err->T("Warning: Video memory FULL, reducing upscaling and switching to slow caching mode"), 2.0f);
} else {
host->NotifyUserMessage(err->T("Warning: Video memory FULL, switching to slow caching mode"), 2.0f);
}
scaleFactor = 1;
actualFmt = dstFmt;
allocSuccess = image->CreateDirect(cmdInit, w * scaleFactor, h * scaleFactor, maxLevelToGenerate + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping);
}
if (!allocSuccess) {
ERROR_LOG(G3D, "Failed to create texture (%dx%d)", w, h);
delete entry->vkTex;
entry->vkTex = nullptr;
}
if (!entry->vkTex) {
return;
}
ReplacedTextureDecodeInfo replacedInfo;
bool willSaveTex = false;
if (replacer_.Enabled() && !replaced.Valid()) {
if (replacer_.Enabled() && !plan.replaced->Valid()) {
replacedInfo.cachekey = entry->CacheKey();
replacedInfo.hash = entry->fullhash;
replacedInfo.addr = entry->addr;
replacedInfo.isVideo = isVideo;
replacedInfo.isVideo = plan.isVideo;
replacedInfo.isFinal = (entry->status & TexCacheEntry::STATUS_TO_SCALE) == 0;
replacedInfo.scaleFactor = scaleFactor;
replacedInfo.scaleFactor = plan.scaleFactor;
replacedInfo.fmt = FromVulkanFormat(actualFmt);
willSaveTex = replacer_.WillSave(replacedInfo);
}
if (entry->vkTex) {
VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
"Texture Upload (%08x) video=%d", entry->addr, isVideo);
VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
"Texture Upload (%08x) video=%d", entry->addr, plan.isVideo);
// NOTE: Since the level is not part of the cache key, we assume it never changes.
// Upload the texture data.
for (int i = 0; i < plan.levelsToLoad; i++) {
int mipUnscaledWidth = gstate.getTextureWidth(i);
int mipUnscaledHeight = gstate.getTextureHeight(i);
u8 level = 0;
bool fakeMipmap = false;
if (IsFakeMipmapChange()) {
level = std::max(0, gstate.getTexLevelOffset16() / 16);
fakeMipmap = level > 0;
int mipWidth = mipUnscaledWidth * plan.scaleFactor;
int mipHeight = mipUnscaledHeight * plan.scaleFactor;
if (plan.replaced->Valid()) {
plan.replaced->GetSize(i, mipWidth, mipHeight);
}
// Upload the texture data.
for (int i = 0; i <= maxLevel; i++) {
int mipUnscaledWidth = gstate.getTextureWidth(i);
int mipUnscaledHeight = gstate.getTextureHeight(i);
int bpp = actualFmt == VULKAN_8888_FORMAT ? 4 : 2; // output bpp
int stride = (mipWidth * bpp + 15) & ~15; // output stride
int size = stride * mipHeight;
uint32_t bufferOffset;
VkBuffer texBuf;
// NVIDIA reports a min alignment of 1 but that can't be healthy... let's align by 16 as a minimum.
int pushAlignment = std::max(16, (int)vulkan->GetPhysicalDeviceProperties().properties.limits.optimalBufferCopyOffsetAlignment);
void *data;
std::vector<uint8_t> saveData;
int mipWidth = mipUnscaledWidth * scaleFactor;
int mipHeight = mipUnscaledHeight * scaleFactor;
if (replaced.Valid()) {
replaced.GetSize(i, mipWidth, mipHeight);
auto loadLevel = [&](int sz, int srcLevel, int lstride, int lfactor) {
if (willSaveTex) {
saveData.resize(sz);
data = &saveData[0];
} else {
data = drawEngine_->GetPushBufferForTextureData()->PushAligned(sz, &bufferOffset, &texBuf, pushAlignment);
}
LoadTextureLevel(*entry, (uint8_t *)data, lstride, srcLevel, lfactor, dstFmt);
if (willSaveTex)
bufferOffset = drawEngine_->GetPushBufferForTextureData()->PushAligned(&saveData[0], sz, pushAlignment, &texBuf);
};
int bpp = actualFmt == VULKAN_8888_FORMAT ? 4 : 2; // output bpp
int stride = (mipWidth * bpp + 15) & ~15; // output stride
int size = stride * mipHeight;
uint32_t bufferOffset;
VkBuffer texBuf;
// NVIDIA reports a min alignment of 1 but that can't be healthy... let's align by 16 as a minimum.
int pushAlignment = std::max(16, (int)vulkan->GetPhysicalDeviceProperties().properties.limits.optimalBufferCopyOffsetAlignment);
void *data;
std::vector<uint8_t> saveData;
bool dataScaled = true;
if (plan.replaced->Valid()) {
// Directly load the replaced image.
data = drawEngine_->GetPushBufferForTextureData()->PushAligned(size, &bufferOffset, &texBuf, pushAlignment);
double replaceStart = time_now_d();
plan.replaced->Load(i, data, stride); // if it fails, it'll just be garbage data... OK for now.
replacementTimeThisFrame_ += time_now_d() - replaceStart;
VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT,
"Copy Upload (replaced): %dx%d", mipWidth, mipHeight);
entry->vkTex->UploadMip(cmdInit, i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp);
VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT);
} else {
if (computeUpload) {
int srcBpp = dstFmt == VULKAN_8888_FORMAT ? 4 : 2;
int srcStride = mipUnscaledWidth * srcBpp;
int srcSize = srcStride * mipUnscaledHeight;
loadLevel(srcSize, i == 0 ? plan.baseLevelSrc : i, srcStride, 1);
dataScaled = false;
auto loadLevel = [&](int sz, int level, int lstride, int lfactor) {
if (willSaveTex) {
saveData.resize(sz);
data = &saveData[0];
} else {
data = drawEngine_->GetPushBufferForTextureData()->PushAligned(sz, &bufferOffset, &texBuf, pushAlignment);
}
LoadTextureLevel(*entry, (uint8_t *)data, lstride, level, lfactor, dstFmt);
if (willSaveTex)
bufferOffset = drawEngine_->GetPushBufferForTextureData()->PushAligned(&saveData[0], sz, pushAlignment, &texBuf);
};
bool dataScaled = true;
if (replaced.Valid()) {
// Directly load the replaced image.
data = drawEngine_->GetPushBufferForTextureData()->PushAligned(size, &bufferOffset, &texBuf, pushAlignment);
double replaceStart = time_now_d();
replaced.Load(i, data, stride); // if it fails, it'll just be garbage data... OK for now.
replacementTimeThisFrame_ += time_now_d() - replaceStart;
// This format can be used with storage images.
VkImageView view = entry->vkTex->CreateViewForMip(i);
VkDescriptorSet descSet = computeShaderManager_.GetDescriptorSet(view, texBuf, bufferOffset, srcSize);
struct Params { int x; int y; } params{ mipUnscaledWidth, mipUnscaledHeight };
VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
"Compute Upload: %dx%d->%dx%d", mipUnscaledWidth, mipUnscaledHeight, mipWidth, mipHeight);
vkCmdBindPipeline(cmdInit, VK_PIPELINE_BIND_POINT_COMPUTE, computeShaderManager_.GetPipeline(uploadCS_));
vkCmdBindDescriptorSets(cmdInit, VK_PIPELINE_BIND_POINT_COMPUTE, computeShaderManager_.GetPipelineLayout(), 0, 1, &descSet, 0, nullptr);
vkCmdPushConstants(cmdInit, computeShaderManager_.GetPipelineLayout(), VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(params), &params);
vkCmdDispatch(cmdInit, (mipUnscaledWidth + 7) / 8, (mipUnscaledHeight + 7) / 8, 1);
VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
vulkan->Delete().QueueDeleteImageView(view);
} else {
loadLevel(size, i == 0 ? plan.baseLevelSrc : i, stride, plan.scaleFactor);
VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT,
"Copy Upload (replaced): %dx%d", mipWidth, mipHeight);
"Copy Upload: %dx%d", mipWidth, mipHeight);
entry->vkTex->UploadMip(cmdInit, i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp);
VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT);
} else {
if (fakeMipmap) {
loadLevel(size, i, stride, scaleFactor);
entry->vkTex->UploadMip(cmdInit, 0, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp);
} else {
if (computeUpload) {
int srcBpp = dstFmt == VULKAN_8888_FORMAT ? 4 : 2;
int srcStride = mipUnscaledWidth * srcBpp;
int srcSize = srcStride * mipUnscaledHeight;
loadLevel(srcSize, i, srcStride, 1);
dataScaled = false;
// This format can be used with storage images.
VkImageView view = entry->vkTex->CreateViewForMip(i);
VkDescriptorSet descSet = computeShaderManager_.GetDescriptorSet(view, texBuf, bufferOffset, srcSize);
struct Params { int x; int y; } params{ mipUnscaledWidth, mipUnscaledHeight };
VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
"Compute Upload: %dx%d->%dx%d", mipUnscaledWidth, mipUnscaledHeight, mipWidth, mipHeight);
vkCmdBindPipeline(cmdInit, VK_PIPELINE_BIND_POINT_COMPUTE, computeShaderManager_.GetPipeline(uploadCS_));
vkCmdBindDescriptorSets(cmdInit, VK_PIPELINE_BIND_POINT_COMPUTE, computeShaderManager_.GetPipelineLayout(), 0, 1, &descSet, 0, nullptr);
vkCmdPushConstants(cmdInit, computeShaderManager_.GetPipelineLayout(), VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(params), &params);
vkCmdDispatch(cmdInit, (mipUnscaledWidth + 7) / 8, (mipUnscaledHeight + 7) / 8, 1);
VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
vulkan->Delete().QueueDeleteImageView(view);
} else {
loadLevel(size, i, stride, scaleFactor);
VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT,
"Copy Upload: %dx%d", mipWidth, mipHeight);
entry->vkTex->UploadMip(cmdInit, i, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp);
VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT);
}
}
if (replacer_.Enabled()) {
// When hardware texture scaling is enabled, this saves the original.
int w = dataScaled ? mipWidth : mipUnscaledWidth;
int h = dataScaled ? mipHeight : mipUnscaledHeight;
// At this point, data should be saveData, and not slow.
replacer_.NotifyTextureDecoded(replacedInfo, data, stride, i, w, h);
}
}
if (replacer_.Enabled()) {
// When hardware texture scaling is enabled, this saves the original.
int w = dataScaled ? mipWidth : mipUnscaledWidth;
int h = dataScaled ? mipHeight : mipUnscaledHeight;
// At this point, data should be saveData, and not slow.
replacer_.NotifyTextureDecoded(replacedInfo, data, stride, i, w, h);
}
}
}
VkImageLayout layout = computeUpload ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
VkPipelineStageFlags prevStage = computeUpload ? VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT : VK_PIPELINE_STAGE_TRANSFER_BIT;
VkImageLayout layout = computeUpload ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
VkPipelineStageFlags prevStage = computeUpload ? VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT : VK_PIPELINE_STAGE_TRANSFER_BIT;
// Generate any additional mipmap levels.
// This will transition the whole stack to GENERAL if it wasn't already.
if (maxLevel != maxLevelToGenerate) {
VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT, "Mipgen up to level %d", maxLevelToGenerate);
entry->vkTex->GenerateMips(cmdInit, maxLevel + 1, computeUpload);
layout = VK_IMAGE_LAYOUT_GENERAL;
prevStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT);
}
// Generate any additional mipmap levels.
// This will transition the whole stack to GENERAL if it wasn't already.
if (plan.levelsToLoad < plan.levelsToCreate) {
VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT, "Mipgen up to level %d", plan.levelsToCreate);
entry->vkTex->GenerateMips(cmdInit, plan.levelsToLoad, computeUpload);
layout = VK_IMAGE_LAYOUT_GENERAL;
prevStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT);
}
if (maxLevel == 0) {
entry->status |= TexCacheEntry::STATUS_BAD_MIPS;
} else {
entry->status &= ~TexCacheEntry::STATUS_BAD_MIPS;
}
if (replaced.Valid()) {
entry->SetAlphaStatus(TexCacheEntry::TexStatus(replaced.AlphaStatus()));
}
entry->vkTex->EndCreate(cmdInit, false, prevStage, layout);
VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
entry->vkTex->EndCreate(cmdInit, false, prevStage, layout);
VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
if (plan.replaced->Valid()) {
entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus()));
}
}
@ -979,60 +845,54 @@ void TextureCacheVulkan::LoadTextureLevel(TexCacheEntry &entry, uint8_t *writePt
int w = gstate.getTextureWidth(level);
int h = gstate.getTextureHeight(level);
gpuStats.numTexturesDecoded++;
GETextureFormat tfmt = (GETextureFormat)entry.format;
GEPaletteFormat clutformat = gstate.getClutPaletteFormat();
u32 texaddr = gstate.getTextureAddress(level);
{
PROFILE_THIS_SCOPE("decodetex");
_assert_msg_(texaddr != 0, "Can't load a texture from address null")
GETextureFormat tfmt = (GETextureFormat)entry.format;
GEPaletteFormat clutformat = gstate.getClutPaletteFormat();
u32 texaddr = gstate.getTextureAddress(level);
int bufw = GetTextureBufw(level, texaddr, tfmt);
int bpp = dstFmt == VULKAN_8888_FORMAT ? 4 : 2;
_assert_msg_(texaddr != 0, "Can't load a texture from address null")
u32 *pixelData;
int decPitch;
int bufw = GetTextureBufw(level, texaddr, tfmt);
int bpp = dstFmt == VULKAN_8888_FORMAT ? 4 : 2;
bool expand32 = !gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS) || scaleFactor > 1;
u32 *pixelData = (u32 *)writePtr;
int decPitch = rowPitch;
if (scaleFactor > 1) {
tmpTexBufRearrange_.resize(std::max(bufw, w) * h);
pixelData = tmpTexBufRearrange_.data();
// We want to end up with a neatly packed texture for scaling.
decPitch = w * bpp;
} else {
pixelData = (u32 *)writePtr;
decPitch = rowPitch;
}
if (scaleFactor > 1) {
tmpTexBufRearrange_.resize(std::max(bufw, w) * h);
pixelData = tmpTexBufRearrange_.data();
// We want to end up with a neatly packed texture for scaling.
decPitch = w * bpp;
}
CheckAlphaResult alphaResult = DecodeTextureLevel((u8 *)pixelData, decPitch, tfmt, clutformat, texaddr, level, bufw, false, expand32);
entry.SetAlphaStatus(alphaResult, level);
bool expand32 = !gstate_c.Supports(GPU_SUPPORTS_16BIT_FORMATS) || dstFmt == VK_FORMAT_R8G8B8A8_UNORM;
if (scaleFactor > 1) {
u32 fmt = dstFmt;
// CPU scaling reads from the destination buffer so we want cached RAM.
uint8_t *rearrange = (uint8_t *)AllocateAlignedMemory(w * scaleFactor * h * scaleFactor * 4, 16);
scaler_.ScaleAlways((u32 *)rearrange, pixelData, w, h, scaleFactor);
pixelData = (u32 *)writePtr;
CheckAlphaResult alphaResult = DecodeTextureLevel((u8 *)pixelData, decPitch, tfmt, clutformat, texaddr, level, bufw, false, false, expand32);
// We always end up at 8888. Other parts assume this.
_assert_(dstFmt == VULKAN_8888_FORMAT);
bpp = sizeof(u32);
decPitch = w * bpp;
// WARN_LOG(G3D, "Alpha: full=%d w=%d h=%d level=%d %s/%s", (int)(alphaResult == CHECKALPHA_FULL), w, h, level, GeTextureFormatToString(tfmt), GEPaletteFormatToString(clutformat));
entry.SetAlphaStatus(alphaResult, level);
if (scaleFactor > 1) {
u32 fmt = dstFmt;
// CPU scaling reads from the destination buffer so we want cached RAM.
uint8_t *rearrange = (uint8_t *)AllocateAlignedMemory(w * scaleFactor * h * scaleFactor * 4, 16);
scaler.ScaleAlways((u32 *)rearrange, pixelData, fmt, w, h, scaleFactor);
pixelData = (u32 *)writePtr;
dstFmt = (VkFormat)fmt;
// We always end up at 8888. Other parts assume this.
_assert_(dstFmt == VULKAN_8888_FORMAT);
bpp = sizeof(u32);
decPitch = w * bpp;
if (decPitch != rowPitch) {
for (int y = 0; y < h; ++y) {
memcpy(writePtr + rowPitch * y, rearrange + decPitch * y, w * bpp);
}
decPitch = rowPitch;
} else {
memcpy(writePtr, rearrange, w * h * 4);
if (decPitch != rowPitch) {
for (int y = 0; y < h; ++y) {
memcpy(writePtr + rowPitch * y, rearrange + decPitch * y, w * bpp);
}
FreeAlignedMemory(rearrange);
decPitch = rowPitch;
} else {
memcpy(writePtr, rearrange, w * h * 4);
}
FreeAlignedMemory(rearrange);
}
}

View File

@ -21,7 +21,6 @@
#include "GPU/GPUInterface.h"
#include "GPU/GPUState.h"
#include "Common/GPU/Vulkan/VulkanContext.h"
#include "GPU/Vulkan/TextureScalerVulkan.h"
#include "GPU/Common/TextureCacheCommon.h"
#include "GPU/Vulkan/VulkanUtil.h"
@ -122,8 +121,6 @@ private:
SamplerCache samplerCache_;
TextureScalerVulkan scaler;
DepalShaderCacheVulkan *depalShaderCache_;
ShaderManagerVulkan *shaderManagerVulkan_;
DrawEngineVulkan *drawEngine_;

View File

@ -1,60 +0,0 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <algorithm>
#include "Common/CommonTypes.h"
#include "Common/Data/Convert/ColorConv.h"
#include "Common/GPU/Vulkan/VulkanContext.h"
#include "Common/Log.h"
#include "Common/Thread/ParallelLoop.h"
#include "Core/ThreadPools.h"
#include "GPU/Common/TextureScalerCommon.h"
#include "GPU/Vulkan/TextureScalerVulkan.h"
#include "GPU/Vulkan/VulkanUtil.h"
int TextureScalerVulkan::BytesPerPixel(u32 format) {
return (format == VULKAN_8888_FORMAT) ? 4 : 2;
}
u32 TextureScalerVulkan::Get8888Format() {
return VULKAN_8888_FORMAT;
}
void TextureScalerVulkan::ConvertTo8888(u32 format, u32* source, u32* &dest, int width, int height) {
switch (format) {
case VULKAN_8888_FORMAT:
dest = source; // already fine
break;
case VULKAN_4444_FORMAT:
ParallelRangeLoop(&g_threadManager, std::bind(&convert4444_dx9, (u16*)source, dest, width, std::placeholders::_1, std::placeholders::_2), 0, height, MIN_TEXSCALE_LINES_PER_THREAD);
break;
case VULKAN_565_FORMAT:
ParallelRangeLoop(&g_threadManager, std::bind(&convert565_dx9, (u16*)source, dest, width, std::placeholders::_1, std::placeholders::_2), 0, height, MIN_TEXSCALE_LINES_PER_THREAD);
break;
case VULKAN_1555_FORMAT:
ParallelRangeLoop(&g_threadManager, std::bind(&convert5551_dx9, (u16*)source, dest, width, std::placeholders::_1, std::placeholders::_2), 0, height, MIN_TEXSCALE_LINES_PER_THREAD);
break;
default:
dest = source;
ERROR_LOG(G3D, "iXBRZTexScaling: unsupported texture format");
}
}

View File

@ -1,28 +0,0 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include "Common/CommonTypes.h"
#include "GPU/Common/TextureScalerCommon.h"
class TextureScalerVulkan : public TextureScalerCommon {
protected:
void ConvertTo8888(u32 format, u32* source, u32* &dest, int width, int height) override;
int BytesPerPixel(u32 format) override;
u32 Get8888Format() override;
};

View File

@ -25,6 +25,16 @@ If you want to download regularly updated builds for Android, Windows x86 and x6
For game compatibility, see [community compatibility feedback](https://report.ppsspp.org/games).
What's new in 1.13.1
====================
* Confirmation dialog added before change of MAC address ([#15738])
* IR interpreter regression fixed ([#15739])
* Fix clearing of replacement texture cache ([#15740])
* Improved Portuguese-pt translation ([#15734])
* Fix graphical regression in Split/Second ([#15733])
* Couple of minor crash fixes
What's new in 1.13
==================
@ -502,4 +512,9 @@ Credit goes to:
[#15687]: https://github.com/hrydgard/ppsspp/issues/15687 "Add Zettai Zetsumei Toshi 3"
[#7295]: https://github.com/hrydgard/ppsspp/issues/7295 "Juiced 2: Hot Import Nights, screen artifacts and missing half of race tracks"
[#15717]: https://github.com/hrydgard/ppsspp/issues/15717 "Allows \"merging\" render targets that overlap on the Y axis. Fixes Juiced 2"
[#15698]: https://github.com/hrydgard/ppsspp/issues/15698 "Osk: Allow upper/lower for all keyboards"
[#15698]: https://github.com/hrydgard/ppsspp/issues/15698 "Osk: Allow upper/lower for all keyboards"
[#15738]: https://github.com/hrydgard/ppsspp/issues/15738 "Add confirmation dialog when generating a new Mac address"
[#15739]: https://github.com/hrydgard/ppsspp/issues/15739 "irjit: Correct another PurgeTemps case"
[#15740]: https://github.com/hrydgard/ppsspp/issues/15740 "Replacement: Clear cache on disable"
[#15734]: https://github.com/hrydgard/ppsspp/issues/15734 "Better pt-pt translation"
[#15733]: https://github.com/hrydgard/ppsspp/issues/15733 "Fix bug in blue-to-alpha - alpha blending could be on when it shouldn't be."

View File

@ -329,13 +329,10 @@ void GPUDriverTestScreen::DiscardTest() {
dsDesc.depthWriteEnabled = true;
dsDesc.depthCompare = Comparison::ALWAYS;
dsDesc.stencilEnabled = true;
dsDesc.front.compareMask = 0xFF;
dsDesc.front.compareOp = Comparison::ALWAYS;
dsDesc.front.passOp = StencilOp::REPLACE;
dsDesc.front.failOp = StencilOp::REPLACE; // These two shouldn't matter, because the test that fails is discard, not stencil.
dsDesc.front.depthFailOp = StencilOp::REPLACE;
dsDesc.front.writeMask = 0xFF;
dsDesc.back = dsDesc.front;
dsDesc.stencil.compareOp = Comparison::ALWAYS;
dsDesc.stencil.passOp = StencilOp::REPLACE;
dsDesc.stencil.failOp = StencilOp::REPLACE; // These two shouldn't matter, because the test that fails is discard, not stencil.
dsDesc.stencil.depthFailOp = StencilOp::REPLACE;
DepthStencilState *depthStencilWrite = draw->CreateDepthStencilState(dsDesc);
// Write only depth.
@ -353,33 +350,27 @@ void GPUDriverTestScreen::DiscardTest() {
dsDesc.depthTestEnabled = true;
dsDesc.stencilEnabled = true;
dsDesc.depthCompare = Comparison::ALWAYS;
dsDesc.front.compareOp = Comparison::EQUAL;
dsDesc.front.failOp = StencilOp::KEEP;
dsDesc.front.depthFailOp = StencilOp::KEEP;
dsDesc.front.writeMask = 0x0;
dsDesc.back = dsDesc.front;
dsDesc.stencil.compareOp = Comparison::EQUAL;
dsDesc.stencil.failOp = StencilOp::KEEP;
dsDesc.stencil.depthFailOp = StencilOp::KEEP;
DepthStencilState *stencilEqualDepthAlways = draw->CreateDepthStencilState(dsDesc);
dsDesc.depthTestEnabled = false;
dsDesc.front.compareOp = Comparison::EQUAL;
dsDesc.back = dsDesc.front;
dsDesc.stencil.compareOp = Comparison::EQUAL;
DepthStencilState *stencilEqual = draw->CreateDepthStencilState(dsDesc);
dsDesc.depthTestEnabled = true;
dsDesc.depthCompare = Comparison::ALWAYS;
dsDesc.front.compareOp = Comparison::NOT_EQUAL;
dsDesc.back = dsDesc.front;
dsDesc.stencil.compareOp = Comparison::NOT_EQUAL;
DepthStencilState *stenciNotEqualDepthAlways = draw->CreateDepthStencilState(dsDesc);
dsDesc.depthTestEnabled = false;
dsDesc.front.compareOp = Comparison::NOT_EQUAL;
dsDesc.back = dsDesc.front;
dsDesc.stencil.compareOp = Comparison::NOT_EQUAL;
DepthStencilState *stencilNotEqual = draw->CreateDepthStencilState(dsDesc);
dsDesc.stencilEnabled = true;
dsDesc.depthTestEnabled = true;
dsDesc.front.compareOp = Comparison::ALWAYS;
dsDesc.back = dsDesc.front;
dsDesc.stencil.compareOp = Comparison::ALWAYS;
dsDesc.depthCompare = Comparison::LESS_EQUAL;
DepthStencilState *stencilAlwaysDepthTestLessEqual = draw->CreateDepthStencilState(dsDesc);
dsDesc.depthCompare = Comparison::GREATER;
@ -506,27 +497,28 @@ void GPUDriverTestScreen::DiscardTest() {
dc.BeginPipeline(writePipelines[j], samplerNearest_);
// Draw the rectangle with stencil value 0, depth 0.1f and the text with stencil 0xFF, depth 0.9. Then set 0xFF as the stencil value and draw the rectangles at depth 0.5.
draw->SetStencilRef(0x0);
draw->SetStencilParams(0, 0xFF, 0xFF);
dc.SetCurZ(0.1f);
dc.FillRect(UI::Drawable(bgColorBAD), bounds);
// test bounds
dc.Flush();
draw->SetStencilRef(0xff);
draw->SetStencilParams(0xff, 0xFF, 0xFF);
dc.SetCurZ(0.9f);
dc.DrawTextRect("TEST OK", bounds, textColorBAD, ALIGN_HCENTER | ALIGN_VCENTER | FLAG_DYNAMIC_ASCII);
dc.Flush();
// Draw rectangle that should result in the text
dc.BeginPipeline(testPipeline1[i], samplerNearest_);
draw->SetStencilRef(0xff);
draw->SetStencilParams(0xff, 0, 0xFF);
dc.SetCurZ(0.5f);
dc.FillRect(UI::Drawable(textColorOK), bounds);
dc.Flush();
// Draw rectangle that should result in the bg
dc.BeginPipeline(testPipeline2[i], samplerNearest_);
draw->SetStencilRef(0xff);
draw->SetStencilParams(0xff, 0, 0xFF);
dc.SetCurZ(0.5f);
dc.FillRect(UI::Drawable(bgColorOK), bounds);
dc.Flush();

View File

@ -1654,7 +1654,17 @@ UI::EventReturn GameSettingsScreen::OnChangeproAdhocServerAddress(UI::EventParam
}
UI::EventReturn GameSettingsScreen::OnChangeMacAddress(UI::EventParams &e) {
g_Config.sMACAddress = CreateRandMAC();
auto n = GetI18NCategory("Networking");
auto di = GetI18NCategory("Dialog");
auto confirmScreen = new PromptScreen(
n->T("Change Mac Address"), di->T("Yes"), di->T("No"),
[&](bool success) {
if (success) {
g_Config.sMACAddress = CreateRandMAC();
}}
);
screenManager()->push(confirmScreen);
return UI::EVENT_DONE;
}

View File

@ -410,7 +410,6 @@
<ClInclude Include="..\..\GPU\D3D11\ShaderManagerD3D11.h" />
<ClInclude Include="..\..\GPU\D3D11\StateMappingD3D11.h" />
<ClInclude Include="..\..\GPU\D3D11\TextureCacheD3D11.h" />
<ClInclude Include="..\..\GPU\D3D11\TextureScalerD3D11.h" />
<ClInclude Include="..\..\GPU\Debugger\Breakpoints.h" />
<ClInclude Include="..\..\GPU\Debugger\Debugger.h" />
<ClInclude Include="..\..\GPU\Debugger\Playback.h" />
@ -473,7 +472,6 @@
<ClCompile Include="..\..\GPU\D3D11\StateMappingD3D11.cpp" />
<ClCompile Include="..\..\GPU\D3D11\StencilBufferD3D11.cpp" />
<ClCompile Include="..\..\GPU\D3D11\TextureCacheD3D11.cpp" />
<ClCompile Include="..\..\GPU\D3D11\TextureScalerD3D11.cpp" />
<ClCompile Include="..\..\GPU\Debugger\Breakpoints.cpp" />
<ClCompile Include="..\..\GPU\Debugger\Debugger.cpp" />
<ClCompile Include="..\..\GPU\Debugger\Playback.cpp" />
@ -528,4 +526,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@ -32,7 +32,6 @@
<ClCompile Include="..\..\GPU\D3D11\StateMappingD3D11.cpp" />
<ClCompile Include="..\..\GPU\D3D11\StencilBufferD3D11.cpp" />
<ClCompile Include="..\..\GPU\D3D11\TextureCacheD3D11.cpp" />
<ClCompile Include="..\..\GPU\D3D11\TextureScalerD3D11.cpp" />
<ClCompile Include="..\..\GPU\Debugger\Breakpoints.cpp" />
<ClCompile Include="..\..\GPU\Debugger\Debugger.cpp" />
<ClCompile Include="..\..\GPU\Debugger\Playback.cpp" />
@ -89,7 +88,6 @@
<ClInclude Include="..\..\GPU\D3D11\ShaderManagerD3D11.h" />
<ClInclude Include="..\..\GPU\D3D11\StateMappingD3D11.h" />
<ClInclude Include="..\..\GPU\D3D11\TextureCacheD3D11.h" />
<ClInclude Include="..\..\GPU\D3D11\TextureScalerD3D11.h" />
<ClInclude Include="..\..\GPU\Debugger\Breakpoints.h" />
<ClInclude Include="..\..\GPU\Debugger\Debugger.h" />
<ClInclude Include="..\..\GPU\Debugger\Playback.h" />
@ -120,4 +118,4 @@
<ClInclude Include="..\..\GPU\Common\VertexShaderGenerator.h" />
<ClInclude Include="..\..\GPU\Common\ReinterpretFramebuffer.h" />
</ItemGroup>
</Project>
</Project>

View File

@ -12,12 +12,13 @@
#include "Core/MIPS/MIPSAsm.h"
#include "Core/MIPS/MIPSAnalyst.h"
#include "Core/Config.h"
#include "Core/Debugger/SymbolMap.h"
#include "Core/Reporting.h"
#include "Common/StringUtils.h"
#include "Windows/Debugger/CtrlDisAsmView.h"
#include "Windows/Debugger/Debugger_MemoryDlg.h"
#include "Windows/Debugger/DebuggerShared.h"
#include "Windows/Debugger/BreakpointWindow.h"
#include "Core/Debugger/SymbolMap.h"
#include "Windows/main.h"
#include "Common/CommonWindows.h"
@ -303,6 +304,7 @@ void CtrlDisAsmView::assembleOpcode(u32 address, std::string defaultText)
if (strcasecmp(debugger->GetRegName(cat,reg),registerName.c_str()) == 0)
{
debugger->SetRegValue(cat,reg,value);
Reporting::NotifyDebugger();
SendMessage(GetParent(wnd),WM_DEB_UPDATE,0,0);
return;
}
@ -314,6 +316,7 @@ void CtrlDisAsmView::assembleOpcode(u32 address, std::string defaultText)
}
result = MIPSAsm::MipsAssembleOpcode(op.c_str(),debugger,address);
Reporting::NotifyDebugger();
if (result == true)
{
scanFunctions();

View File

@ -6,12 +6,13 @@
#include <iomanip>
#include "ext/xxhash.h"
#include "Core/Config.h"
#include "Windows/resource.h"
#include "Core/MemMap.h"
#include "Core/Reporting.h"
#include "Windows/W32Util/ContextMenu.h"
#include "Windows/W32Util/Misc.h"
#include "Windows/InputBox.h"
#include "Windows/main.h"
#include "Windows/resource.h"
#include "Common/System/Display.h"
#include "Debugger_Disasm.h"
@ -462,6 +463,7 @@ void CtrlMemView::onChar(WPARAM wParam, LPARAM lParam)
}
}
Reporting::NotifyDebugger();
if (active) Core_EnableStepping(false);
}

View File

@ -5,16 +5,17 @@
#include "Common/System/Display.h"
#include "Common/Data/Encoding/Utf8.h"
#include "Windows/resource.h"
#include "Core/Config.h"
#include "Core/MemMap.h"
#include "Core/Reporting.h"
#include "Windows/W32Util/ContextMenu.h"
#include "Windows/W32Util/Misc.h"
#include "Windows/InputBox.h"
#include "Windows/resource.h"
#include "CtrlRegisterList.h"
#include "Debugger_MemoryDlg.h"
#include "Core/Config.h"
#include "Debugger_Disasm.h"
#include "DebuggerShared.h"
@ -443,6 +444,7 @@ void CtrlRegisterList::editRegisterValue()
cpu->SetRegValue(category, reg, val);
break;
}
Reporting::NotifyDebugger();
redraw();
SendMessage(GetParent(wnd),WM_DEB_UPDATE,0,0); // registers changed -> disassembly needs to be updated
}

View File

@ -49,6 +49,7 @@
#include "Core/KeyMap.h"
#include "Core/MIPS/JitCommon/JitCommon.h"
#include "Core/MIPS/JitCommon/JitBlockCache.h"
#include "Core/Reporting.h"
#include "Windows/InputBox.h"
#include "Windows/InputDevice.h"
#if PPSSPP_API(ANY_GL)
@ -87,6 +88,8 @@
int verysleepy__useSendMessage = 1;
const UINT WM_VERYSLEEPY_MSG = WM_APP + 0x3117;
const UINT WM_USER_GET_BASE_POINTER = WM_APP + 0x3118; // 0xB118
// Respond TRUE to a message with this param value to indicate support.
const WPARAM VERYSLEEPY_WPARAM_SUPPORTED = 0;
// Respond TRUE to a message wit this param value after filling in the addr name.
@ -736,6 +739,18 @@ namespace MainWindow
}
break;
case WM_USER_GET_BASE_POINTER:
Reporting::NotifyDebugger();
switch (lParam) {
case 0:
return (u32)(u64)Memory::base;
case 1:
return (u32)((u64)Memory::base >> 32);
default:
return 0;
}
break;
case WM_GETMINMAXINFO:
{
MINMAXINFO *minmax = reinterpret_cast<MINMAXINFO *>(lParam);

View File

@ -23,7 +23,6 @@
#include "Common/GPU/Vulkan/VulkanLoader.h"
#include "Common/StringUtils.h"
#if PPSSPP_API(ANY_GL)
#include "GPU/GLES/TextureScalerGLES.h"
#include "GPU/GLES/TextureCacheGLES.h"
#include "GPU/GLES/FramebufferManagerGLES.h"
#endif

View File

@ -3,8 +3,8 @@
xmlns:tools="http://schemas.android.com/tools"
package="org.ppsspp.ppsspp"
android:installLocation="auto"
android:versionCode="113000000"
android:versionName="1.13.0.0">
android:versionCode="113010000"
android:versionName="1.13.1.0">
<!-- Note that versionCode should be in the format xyyzzrrrr. Example: 110030000 -->
<!-- In this same case, versionName should be 1.10.3.0 -->
<!-- Also note that we are overriding these values anyway from gradle. -->

20
android/QuestManifest.xml Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature android:name="android.hardware.vr.headtracking" android:version="1" android:required="true" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:logo="@drawable/ic_banner"
android:isGame="true"
android:banner="@drawable/tv_banner"
android:requestLegacyExternalStorage="true"
android:preserveLegacyExternalStorage="true">
<meta-data android:name="com.samsung.android.vr.application.mode" android:value="vr_only"/>
<meta-data android:name="com.oculus.supportedDevices" android:value="quest|quest2"/>
</application>
</manifest>

View File

@ -65,9 +65,6 @@ android {
versionCode ANDROID_VERSION_CODE
versionName ANDROID_VERSION_NAME
}
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
signingConfig signingConfigs.debug
}
buildTypes {
@ -106,6 +103,9 @@ android {
gold {
res.srcDirs = ['gold/res']
}
quest {
manifest.srcFile 'QuestManifest.xml'
}
}
productFlavors {
normal {
@ -122,6 +122,9 @@ android {
'-DANDROID_ARM_NEON=TRUE'
}
}
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
}
gold {
applicationId 'org.ppsspp.ppssppgold'
@ -138,6 +141,28 @@ android {
'-DGOLD=TRUE'
}
}
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
}
quest {
applicationId 'org.ppsspp.ppsspp'
dimension "variant"
externalNativeBuild {
cmake {
// Available arguments listed at https://developer.android.com/ndk/guides/cmake.html
arguments '-DANDROID=true',
'-DANDROID_PLATFORM=android-16',
'-DANDROID_TOOLCHAIN=clang',
'-DANDROID_CPP_FEATURES=',
'-DANDROID_STL=c++_static',
'-DANDROID_ARM_NEON=TRUE',
'-DOPENXR=TRUE'
}
}
ndk {
abiFilters 'arm64-v8a'
}
}
}
variantFilter { variant ->
@ -145,7 +170,10 @@ android {
'normalDebug', // for debugging
'normalOptimized', // for testing
'normalRelease', // for Google Play releases
'goldRelease' // for Google Play releases
'goldRelease', // for Google Play releases
'questDebug', // for VR debugging
'questOptimized', // for VR testing
'questRelease', // for VR releases
]
variant.setIgnore(!needed)
}

View File

@ -135,7 +135,6 @@ VULKAN_FILES := \
$(SRC)/GPU/Vulkan/StateMappingVulkan.cpp \
$(SRC)/GPU/Vulkan/StencilBufferVulkan.cpp \
$(SRC)/GPU/Vulkan/TextureCacheVulkan.cpp \
$(SRC)/GPU/Vulkan/TextureScalerVulkan.cpp \
$(SRC)/GPU/Vulkan/DepalettizeShaderVulkan.cpp \
$(SRC)/GPU/Vulkan/VulkanUtil.cpp \
$(SRC)/GPU/Vulkan/DebugVisVulkan.cpp
@ -358,7 +357,6 @@ EXEC_AND_LIB_FILES := \
$(SRC)/GPU/GLES/StateMappingGLES.cpp.arm \
$(SRC)/GPU/GLES/ShaderManagerGLES.cpp.arm \
$(SRC)/GPU/GLES/FragmentTestCacheGLES.cpp.arm \
$(SRC)/GPU/GLES/TextureScalerGLES.cpp \
$(SRC)/GPU/Software/BinManager.cpp \
$(SRC)/GPU/Software/Clipper.cpp \
$(SRC)/GPU/Software/DrawPixel.cpp.arm \

View File

@ -1102,6 +1102,7 @@ NPEH00029 = true
ULUS10455 = true
[BlueToAlpha]
# Split/Second
ULES01402 = true
ULUS10513 = true
ULJM05812 = true

File diff suppressed because it is too large Load Diff

View File

@ -630,7 +630,7 @@ Audio = Аудио
Controls = Управление
Graphics = Графика
Networking = Сеть
Search = Search
Search = Поиск
System = Системные
Tools = Инструменты
@ -809,7 +809,7 @@ Black border = Черные рамки
Bloom = Свечение
Brightness = Яркость
Cartoon = Мультипликация
CatmullRom = Bicubic (Catmull-Rom) Upscaler
CatmullRom = Бикубический (Catmull-Rom) Апскейлер
ColorCorrection = Цветокоррекция
Contrast = Контрастность
CRT = ЭЛТ-развертка
@ -818,7 +818,7 @@ Gamma = Gamma
Grayscale = Оттенки серого
Intensity = Интенсивность
InverseColors = Инвертированные цвета
MitchellNetravali = Bicubic (Mitchell-Netravali) Upscaler
MitchellNetravali = Бикубический (Mitchell-Netravali) Апскейлер
Natural = Естественные цвета
NaturalA = Естественные цвета (без размытия)
Off = Выключена
@ -1011,7 +1011,7 @@ Untitled PSP game = Безымянная игра для PSP
Clear filter = Clear filter
Filter = Filter
Filtering settings by '%1' = Filtering settings by '%1'
Find settings = Find settings
Find settings = Поиск настроек
No settings matched '%1' = No settings matched '%1'
[Store]
@ -1058,7 +1058,7 @@ High precision int range = Диапазон точных целых чисел
Lang/Region = Язык/регион
Memory Page Size = Размер страницы памяти
Native Resolution = Родное разрешение
No GPU driver bugs detected = No GPU driver bugs detected
No GPU driver bugs detected = Ошибок GPU драйвера не обнаружено
OGL Extensions = Расширения OGL
OpenGL ES 2.0 Extensions = Расширения OpenGL ES 2.0
OpenGL ES 3.0 Extensions = Расширения OpenGL ES 3.0
@ -1094,8 +1094,8 @@ AVI Dump started. = Дамп AVI запущен
AVI Dump stopped. = Дамп AVI остановлен
Cache ISO in RAM = Кэшировать ISO в ОЗУ
Change CPU Clock = Эмулируемая частота ЦП PSP (нестабильно)
Color Saturation = Color Saturation
Color Tint = Color Tint
Color Saturation = Насыщенность
Color Tint = Оттенок Цвета
Memory Stick folder = Изменить папку с картой памяти
Memory Stick size = Изменить размер карты памяти (Гб)
Change Nickname = Изменить ник
@ -1122,7 +1122,7 @@ Enable Compatibility Server Reports = Отправка данных о совм
Error: load undo state is from a different game = Ошибка: восстаналиваемое состояние из другой игры
Failed to load state. Error in the file system. = Не удалось загрузить состояние. Ошибка файловой системы.
Failed to save state. Error in the file system. = Не удалось сохранить состояние. Ошибка файловой системы.
Failed to load state for load undo. Error in the file system. = Не удадлось загрузить восстанавливаемое состояние. Ошибка в файловой системе.
Failed to load state for load undo. Error in the file system. = Не удалось загрузить восстанавливаемое состояние. Ошибка в файловой системе.
Fast (lag on slow storage) = Быстрый
Fast Memory = Быстрая память (нестабильно)
Floating symbols = Парящие символы
@ -1167,7 +1167,7 @@ Savestate slot backups = Резервные копии слота состоян
Screenshots as PNG = Сохранять скриншоты в PNG
Set UI background... = Изменить фон интерфейса...
Show ID = Показывать ID
Show Memory Stick folder = Show Memory Stick folder
Show Memory Stick folder = Показать папку Memory Stick
Show region flag = Показывать флаг региона
Simulate UMD delays = Имитировать задержки UMD
Slot 1 = Слот 1
@ -1177,7 +1177,7 @@ Slot 4 = Слот 4
Slot 5 = Слот 5
Storage full = Диск заполнен
Sustained performance mode = Режим длительной работы
Theme = Theme
Theme = Тема
Time Format = Формат времени
UI = Пользовательский интерфейс
UI Sound = Звуки интерфейса
@ -1196,7 +1196,7 @@ Waves = Волны
YYYYMMDD = ГГГГММДД
[TextureShaders]
Off = Off
Off = Выкл.
TexMMPX = TexMMPX
Tex2xBRZ = Tex2xBRZ
Tex4xBRZ = Tex4xBRZ

View File

@ -367,7 +367,6 @@ SOURCES_CXX += \
$(GPUDIR)/GLES/FragmentTestCacheGLES.cpp \
$(GPUDIR)/GLES/FramebufferManagerGLES.cpp \
$(GPUDIR)/GLES/TextureCacheGLES.cpp \
$(GPUDIR)/GLES/TextureScalerGLES.cpp \
$(GPUDIR)/GLES/ShaderManagerGLES.cpp \
$(GPUDIR)/GLES/StateMappingGLES.cpp \
$(GPUDIR)/GLES/StencilBufferGLES.cpp \
@ -735,7 +734,6 @@ SOURCES_CXX += \
$(GPUDIR)/Vulkan/StateMappingVulkan.cpp \
$(GPUDIR)/Vulkan/StencilBufferVulkan.cpp \
$(GPUDIR)/Vulkan/TextureCacheVulkan.cpp \
$(GPUDIR)/Vulkan/TextureScalerVulkan.cpp \
$(GPUDIR)/Vulkan/VulkanUtil.cpp \
$(LIBRETRODIR)/LibretroVulkanContext.cpp \
$(LIBRETRODIR)/libretro_vulkan.cpp
@ -750,8 +748,7 @@ SOURCES_CXX += \
$(GPUDIR)/Directx9/ShaderManagerDX9.cpp \
$(GPUDIR)/Directx9/StateMappingDX9.cpp \
$(GPUDIR)/Directx9/StencilBufferDX9.cpp \
$(GPUDIR)/Directx9/TextureCacheDX9.cpp \
$(GPUDIR)/Directx9/TextureScalerDX9.cpp
$(GPUDIR)/Directx9/TextureCacheDX9.cpp
SOURCES_CXX += \
$(GPUDIR)/D3D11/DepalettizeShaderD3D11.cpp \
@ -763,7 +760,6 @@ SOURCES_CXX += \
$(GPUDIR)/D3D11/StateMappingD3D11.cpp \
$(GPUDIR)/D3D11/StencilBufferD3D11.cpp \
$(GPUDIR)/D3D11/TextureCacheD3D11.cpp \
$(GPUDIR)/D3D11/TextureScalerD3D11.cpp \
$(LIBRETRODIR)/LibretroD3D11Context.cpp
SOURCES_CXX += \

9
quest/.gitignore vendored
View File

@ -1,9 +0,0 @@
bin
gen
obj
build*/
gen
obj
.cxx
.externalNativeBuild
android.iml

View File

@ -1,127 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.ppsspp.ppsspp"
android:installLocation="auto"
android:versionCode="112030000"
android:versionName="1.12.3.0">
<!-- Note that versionCode should be in the format xyyzzrrrr. Example: 110030000 -->
<!-- In this same case, versionName should be 1.10.3.0 -->
<!-- Also note that we are overriding these values anyway from gradle. -->
<uses-feature android:glEsVersion="0x00020000" />
<uses-feature android:name="android.hardware.vr.headtracking" android:version="1" android:required="true" />
<uses-feature android:name="android.hardware.screen.landscape" android:required="false" />
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
<uses-feature android:name="android.software.leanback" android:required="false" />
<uses-feature android:name="android.hardware.gamepad" android:required="false" />
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
<uses-feature android:name="android.hardware.location.network" android:required="false" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<!-- I tried using android:maxSdkVersion="29" on WRITE/READ_EXTERNAL_STORAGE, but that made
<it so that in legacy mode, you can't ask for permission anymore. So removed that. -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="archos.permission.FULLSCREEN.FULL" />
<uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission-sdk-23 android:name="android.permission.CAMERA" />
<uses-permission-sdk-23 android:name="android.permission.RECORD_AUDIO" />
<!-- AndroidX minimum SDK workaround. We don't care if it's broken on older versions. -->
<uses-sdk tools:overrideLibrary="androidx.appcompat.resources,androidx.appcompat,androidx.fragment,androidx.drawerlayout,androidx.vectordrawable.animated,androidx.vectordrawable,androidx.viewpager,androidx.loader,androidx.activity,androidx.annotation,androidx.customview,androidx.cursoradapter,androidx.arch,androidx.collection,androidx.core,androidx.versionedparcelable,androidx.interpolator,androidx.lifecycle,androidx.loader,androidx.savedstate,androidx.lifecycle.viewmodel,androidx.lifecycle.livedata,androidx.lifecycle.livedata.core,androidx.arch.core,androidx.documentfile"/>
<supports-screens
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:xlargeScreens="true" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:logo="@drawable/ic_banner"
android:isGame="true"
android:banner="@drawable/tv_banner"
android:requestLegacyExternalStorage="true"
android:preserveLegacyExternalStorage="true">
<meta-data android:name="com.samsung.android.vr.application.mode" android:value="vr_only"/>
<meta-data android:name="com.oculus.supportedDevices" android:value="quest|quest2"/>
<activity
android:name=".PpssppActivity"
android:configChanges="locale|keyboard|keyboardHidden|navigation|uiMode"
android:label="@string/app_name"
android:theme="@style/ppsspp_style"
android:exported="true">
<!-- android:screenOrientation="landscape" -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
<category android:name="tv.ouya.intent.category.GAME"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="*"
android:mimeType="*/*"
android:pathPattern=".*\\.iso"
android:scheme="file" />
<data
android:host="*"
android:mimeType="*/*"
android:pathPattern=".*\\.cso"
android:scheme="file" />
<data
android:host="*"
android:mimeType="*/*"
android:pathPattern=".*\\.elf"
android:scheme="file" />
<data
android:host="*"
android:mimeType="*/*"
android:pathPattern=".*\\.ISO"
android:scheme="file" />
<data
android:host="*"
android:mimeType="*/*"
android:pathPattern=".*\\.CSO"
android:scheme="file" />
<data
android:host="*"
android:mimeType="*/*"
android:pathPattern=".*\\.ELF"
android:scheme="file" />
</intent-filter>
</activity>
<meta-data android:name="isGame" android:value="true" />
<activity
android:name=".ShortcutActivity"
android:label="@string/shortcut_name"
android:exported="true">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="android.intent.action.CREATE_SHORTCUT" />
</intent-filter>
</activity>
<meta-data
android:name="xperiaplayoptimized_content"
android:resource="@drawable/ic_launcher" />
</application>
</manifest>

View File

@ -1,164 +0,0 @@
plugins {
id 'com.gladed.androidgitversion' version '0.4.5'
}
apply plugin: 'com.android.application'
androidGitVersion {
codeFormat = "MNNPPBBBB"
format = "%tag%%-count%%-branch%%-dirty%"
prefix = "v" // Only tags beginning with v are considered.
untrackedIsDirty = false
}
dependencies {
def appcompat_version = "1.2.0"
implementation "androidx.appcompat:appcompat:$appcompat_version"
// Convenient wrapper around DocumentContract. Might look into writing our own
// to see if there's some performance to squeeze at some point, but doubt it.
implementation "androidx.documentfile:documentfile:1.0.1"
}
android {
flavorDimensions "variant"
signingConfigs {
debug {
storeFile file("../android/debug.keystore")
}
optimized {
storeFile file("../android/debug.keystore")
}
// Set these in a system global (or project local, but not checked in) gradle.properties .
if (project.hasProperty("RELEASE_STORE_FILE")) {
release {
storeFile file(RELEASE_STORE_FILE)
storePassword RELEASE_STORE_PASSWORD
keyAlias RELEASE_KEY_ALIAS
keyPassword RELEASE_KEY_PASSWORD
}
} else {
release {
}
}
}
compileSdkVersion 32
defaultConfig {
applicationId 'org.ppsspp.ppsspp'
if (androidGitVersion.name() != "unknown" && androidGitVersion.code() >= 14000000) {
// Start using automatic Android version numbers from version 1.4.
println "Overriding Android Version Name, Code: " + androidGitVersion.name() + " " + androidGitVersion.code();
versionName androidGitVersion.name()
versionCode androidGitVersion.code()
} else {
println "(not using these:) Android Version Name, Code: " + androidGitVersion.name() + " " + androidGitVersion.code();
}
new File("versionname.txt").write(androidGitVersion.name())
new File("versioncode.txt").write(androidGitVersion.code().toString())
minSdkVersion 9
targetSdkVersion 32
if (project.hasProperty("ANDROID_VERSION_CODE") && project.hasProperty("ANDROID_VERSION_NAME")) {
versionCode ANDROID_VERSION_CODE
versionName ANDROID_VERSION_NAME
}
ndk {
abiFilters 'arm64-v8a'
}
signingConfig signingConfigs.debug
}
buildTypes {
debug {
minifyEnabled = false
jniDebuggable true
signingConfig signingConfigs.debug
}
optimized {
// Debug signed but optimized.
minifyEnabled = false
jniDebuggable true
signingConfig android.buildTypes.debug.signingConfig
}
release {
minifyEnabled = false
signingConfig signingConfigs.release
}
}
externalNativeBuild {
cmake {
path '../CMakeLists.txt'
}
}
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
res.srcDirs = ['../android/res']
java.srcDirs = ['../android/src']
aidl.srcDirs = ['../android/src']
resources.srcDirs = ['../android/src']
assets.srcDirs = ['../assets']
}
gold {
res.srcDirs = ['../android/gold/res']
}
}
productFlavors {
normal {
applicationId 'org.ppsspp.ppsspp'
dimension "variant"
externalNativeBuild {
cmake {
// Available arguments listed at https://developer.android.com/ndk/guides/cmake.html
arguments '-DANDROID=true',
'-DANDROID_PLATFORM=android-16',
'-DANDROID_TOOLCHAIN=clang',
'-DANDROID_CPP_FEATURES=',
'-DANDROID_STL=c++_static',
'-DANDROID_ARM_NEON=TRUE',
'-DOPENXR=TRUE'
}
}
}
gold {
applicationId 'org.ppsspp.ppssppgold'
dimension "variant"
externalNativeBuild {
cmake {
// Available arguments listed at https://developer.android.com/ndk/guides/cmake.html
arguments '-DANDROID=true',
'-DANDROID_PLATFORM=android-16',
'-DANDROID_TOOLCHAIN=clang',
'-DANDROID_CPP_FEATURES=',
'-DANDROID_STL=c++_static',
'-DANDROID_ARM_NEON=TRUE',
'-DOPENXR=TRUE',
'-DGOLD=TRUE'
}
}
}
}
variantFilter { variant ->
def needed = variant.name in [
'normalDebug', // for debugging
'normalOptimized', // for testing
'normalRelease', // for Google Play releases
'goldRelease' // for Google Play releases
]
variant.setIgnore(!needed)
}
}
afterEvaluate {
android.sourceSets.main.assets.getSrcDirs().each { println it }
}
// F-Droid lite version can be created with : ./gradlew assembleOptimized -Pf_droid
if (project.hasProperty("f_droid")) {
project.android.sourceSets.main.java.srcDirs += '../android/libs/MogaStubs'
} else {
project.dependencies {
implementation files('../android/libs/com.bda.controller.jar')
}
}

View File

@ -1,3 +0,0 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true

View File

@ -1,20 +0,0 @@
# To enable ProGuard in your project, edit project.properties
# to define the proguard.config property as described in that file.
#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in ${sdk.dir}/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@ -1,12 +0,0 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system edit
# "ant.properties", and override values to adapt the script to your
# project structure.
#
# Project target.
target=android-26

View File

@ -1 +1 @@
include ':android',':quest'
include ':android'

View File

@ -92,6 +92,19 @@ static const IRVerification tests[] = {
},
{ &PurgeTemps },
},
{
"Load32LeftPurgeTemps",
{
{ IROp::Mov, { IRTEMP_LR_ADDR }, MIPS_REG_A0 },
{ IROp::AndConst, { IRTEMP_LR_ADDR }, IRTEMP_LR_ADDR, 0, 0xFFFFFFFC },
{ IROp::Load32, { MIPS_REG_V0 }, IRTEMP_LR_ADDR, 0, 0 },
},
{
{ IROp::AndConst, { IRTEMP_LR_ADDR }, MIPS_REG_A0, 0, 0xFFFFFFFC },
{ IROp::Load32, { MIPS_REG_V0 }, IRTEMP_LR_ADDR, 0, 0 },
},
{ &PurgeTemps },
},
{
"SwapClobberTemp",
{