mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-15 23:40:29 +02:00
Merge branch 'master' into feature_openxr_stereo
This commit is contained in:
commit
00aaf9e1a6
62 files changed
+1034
-550
No files matched your search
@@ -3,10 +3,12 @@
|
||||
#include <vector>
|
||||
|
||||
// Insert-only small-set implementation. Performs no allocation unless MaxFastSize is exceeded.
|
||||
// Can also be used as a small vector, then use push_back (or push_in_place) instead of insert.
|
||||
// Duplicates are thus allowed if you use that, but not if you exclusively use insert.
|
||||
template <class T, int MaxFastSize>
|
||||
struct TinySet {
|
||||
~TinySet() { delete slowLookup_; }
|
||||
inline void insert(T t) {
|
||||
inline void insert(const T &t) {
|
||||
// Fast linear scan.
|
||||
for (int i = 0; i < fastCount; i++) {
|
||||
if (fastLookup_[i] == t)
|
||||
@@ -20,6 +22,41 @@ struct TinySet {
|
||||
// Fall back to slow path.
|
||||
insertSlow(t);
|
||||
}
|
||||
inline void push_back(const T &t) {
|
||||
if (fastCount < MaxFastSize) {
|
||||
fastLookup_[fastCount++] = t;
|
||||
return;
|
||||
}
|
||||
if (!slowLookup_) {
|
||||
slowLookup_ = new std::vector<T>();
|
||||
}
|
||||
slowLookup_->push_back(t);
|
||||
}
|
||||
inline T *add_back() {
|
||||
if (fastCount < MaxFastSize) {
|
||||
return &fastLookup_[fastCount++];
|
||||
}
|
||||
if (!slowLookup_) {
|
||||
slowLookup_ = new std::vector<T>();
|
||||
}
|
||||
T t;
|
||||
slowLookup_->push_back(t);
|
||||
return slowLookup_->back();
|
||||
}
|
||||
void append(const TinySet<T, MaxFastSize> &other) {
|
||||
size_t otherSize = other.size();
|
||||
if (size() + otherSize <= MaxFastSize) {
|
||||
// Fast case
|
||||
for (int i = 0; i < otherSize; i++) {
|
||||
fastLookup_[fastCount + i] = other.fastLookup_[i];
|
||||
}
|
||||
fastCount += other.fastCount;
|
||||
} else {
|
||||
for (int i = 0; i < otherSize; i++) {
|
||||
push_back(other[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool contains(T t) const {
|
||||
for (int i = 0; i < fastCount; i++) {
|
||||
if (fastLookup_[i] == t)
|
||||
@@ -52,6 +89,26 @@ struct TinySet {
|
||||
slowLookup_ = nullptr;
|
||||
fastCount = 0;
|
||||
}
|
||||
bool empty() const {
|
||||
return fastCount == 0;
|
||||
}
|
||||
size_t size() const {
|
||||
if (!slowLookup_) {
|
||||
return fastCount;
|
||||
} else {
|
||||
return slowLookup_->size() + MaxFastSize;
|
||||
}
|
||||
}
|
||||
const T &operator[] (size_t index) const {
|
||||
if (index < MaxFastSize) {
|
||||
return fastLookup_[index];
|
||||
} else {
|
||||
return (*slowLookup_)[index - MaxFastSize];
|
||||
}
|
||||
}
|
||||
const T &back() const {
|
||||
return (*this)[size() - 1];
|
||||
}
|
||||
|
||||
private:
|
||||
void insertSlow(T t) {
|
||||
|
||||
@@ -54,7 +54,7 @@ LPD3DBLOB CompileShaderToByteCodeD3D9(const char *code, const char *target, std:
|
||||
}
|
||||
|
||||
bool CompilePixelShaderD3D9(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT3DPIXELSHADER9 *pShader, std::string *errorMessage) {
|
||||
LPD3DBLOB pShaderCode = CompileShaderToByteCodeD3D9(code, "ps_2_0", errorMessage);
|
||||
LPD3DBLOB pShaderCode = CompileShaderToByteCodeD3D9(code, "ps_3_0", errorMessage);
|
||||
if (pShaderCode) {
|
||||
// Create pixel shader.
|
||||
device->CreatePixelShader((DWORD*)pShaderCode->GetBufferPointer(), pShader);
|
||||
@@ -66,7 +66,7 @@ bool CompilePixelShaderD3D9(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT
|
||||
}
|
||||
|
||||
bool CompileVertexShaderD3D9(LPDIRECT3DDEVICE9 device, const char *code, LPDIRECT3DVERTEXSHADER9 *pShader, std::string *errorMessage) {
|
||||
LPD3DBLOB pShaderCode = CompileShaderToByteCodeD3D9(code, "vs_2_0", errorMessage);
|
||||
LPD3DBLOB pShaderCode = CompileShaderToByteCodeD3D9(code, "vs_3_0", errorMessage);
|
||||
if (pShaderCode) {
|
||||
// Create vertex shader.
|
||||
device->CreateVertexShader((DWORD*)pShaderCode->GetBufferPointer(), pShader);
|
||||
|
||||
@@ -1069,11 +1069,7 @@ bool D3D9ShaderModule::Compile(LPDIRECT3DDEVICE9 device, const uint8_t *data, si
|
||||
auto compile = [&](const char *profile) -> HRESULT {
|
||||
return dyn_D3DCompile(source, (UINT)strlen(source), nullptr, defines, includes, "main", profile, 0, 0, &codeBuffer, &errorBuffer);
|
||||
};
|
||||
HRESULT hr = compile(stage_ == ShaderStage::Fragment ? "ps_2_0" : "vs_2_0");
|
||||
if (FAILED(hr) && hr == D3DXERR_INVALIDDATA) {
|
||||
// Might be a post shader. Let's try using shader model 3.
|
||||
hr = compile(stage_ == ShaderStage::Fragment ? "ps_3_0" : "vs_3_0");
|
||||
}
|
||||
HRESULT hr = compile(stage_ == ShaderStage::Fragment ? "ps_3_0" : "vs_3_0");
|
||||
if (FAILED(hr)) {
|
||||
const char *error = errorBuffer ? (const char *)errorBuffer->GetBufferPointer() : "(no errorbuffer returned)";
|
||||
if (hr == ERROR_MOD_NOT_FOUND) {
|
||||
|
||||
@@ -77,5 +77,6 @@ inline bool DataFormatIsColor(DataFormat fmt) {
|
||||
void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format);
|
||||
void ConvertFromBGRA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format);
|
||||
void ConvertToD32F(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format);
|
||||
void ConvertToD16(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format);
|
||||
|
||||
} // namespace
|
||||
@@ -347,6 +347,7 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector<VKRStep *> &st
|
||||
profile->cpuStartTime = time_now_d();
|
||||
|
||||
bool emitLabels = vulkan_->Extensions().EXT_debug_utils;
|
||||
|
||||
for (size_t i = 0; i < steps.size(); i++) {
|
||||
const VKRStep &step = *steps[i];
|
||||
|
||||
@@ -685,7 +686,7 @@ void VulkanQueueRunner::ApplyRenderPassMerge(std::vector<VKRStep *> &steps) {
|
||||
auto mergeRenderSteps = [](VKRStep *dst, VKRStep *src) {
|
||||
// OK. Now, if it's a render, slurp up all the commands and kill the step.
|
||||
// Also slurp up any pretransitions.
|
||||
dst->preTransitions.insert(dst->preTransitions.end(), src->preTransitions.begin(), src->preTransitions.end());
|
||||
dst->preTransitions.append(src->preTransitions);
|
||||
dst->commands.insert(dst->commands.end(), src->commands.begin(), src->commands.end());
|
||||
MergeRenderAreaRectInto(&dst->render.renderArea, src->render.renderArea);
|
||||
// So we don't consider it for other things, maybe doesn't matter.
|
||||
@@ -1060,7 +1061,8 @@ void TransitionFromOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayou
|
||||
}
|
||||
|
||||
void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd) {
|
||||
for (const auto &iter : step.preTransitions) {
|
||||
for (size_t i = 0; i < step.preTransitions.size(); i++) {
|
||||
const TransitionRequest &iter = step.preTransitions[i];
|
||||
if (iter.aspect == VK_IMAGE_ASPECT_COLOR_BIT && iter.fb->color.layout != iter.targetLayout) {
|
||||
recordBarrier_.TransitionImageAuto(
|
||||
iter.fb->color.image,
|
||||
@@ -1151,6 +1153,8 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
|
||||
// The stencil ones are very commonly mostly redundant so let's eliminate them where possible.
|
||||
// Might also want to consider scissor and viewport.
|
||||
VkPipeline lastPipeline = VK_NULL_HANDLE;
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
|
||||
int lastStencilWriteMask = -1;
|
||||
int lastStencilCompareMask = -1;
|
||||
int lastStencilReference = -1;
|
||||
@@ -1166,6 +1170,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
|
||||
VkPipeline pipeline = c.pipeline.pipeline;
|
||||
if (pipeline != lastGraphicsPipeline) {
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
pipelineLayout = c.pipeline.pipelineLayout;
|
||||
lastGraphicsPipeline = pipeline;
|
||||
// Reset dynamic state so it gets refreshed with the new pipeline.
|
||||
lastStencilWriteMask = -1;
|
||||
@@ -1187,6 +1192,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
|
||||
}
|
||||
if (pipeline->pipeline != lastGraphicsPipeline && pipeline->pipeline != VK_NULL_HANDLE) {
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline);
|
||||
pipelineLayout = c.pipeline.pipelineLayout;
|
||||
lastGraphicsPipeline = pipeline->pipeline;
|
||||
// Reset dynamic state so it gets refreshed with the new pipeline.
|
||||
lastStencilWriteMask = -1;
|
||||
@@ -1208,6 +1214,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
|
||||
}
|
||||
if (pipeline->pipeline != lastComputePipeline && pipeline->pipeline != VK_NULL_HANDLE) {
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->pipeline);
|
||||
pipelineLayout = c.pipeline.pipelineLayout;
|
||||
lastComputePipeline = pipeline->pipeline;
|
||||
}
|
||||
break;
|
||||
@@ -1257,7 +1264,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
|
||||
}
|
||||
|
||||
case VKRRenderCommand::PUSH_CONSTANTS:
|
||||
vkCmdPushConstants(cmd, c.push.pipelineLayout, c.push.stages, c.push.offset, c.push.size, c.push.data);
|
||||
vkCmdPushConstants(cmd, pipelineLayout, c.push.stages, c.push.offset, c.push.size, c.push.data);
|
||||
break;
|
||||
|
||||
case VKRRenderCommand::STENCIL:
|
||||
@@ -1276,14 +1283,17 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
|
||||
break;
|
||||
|
||||
case VKRRenderCommand::DRAW_INDEXED:
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.drawIndexed.pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets);
|
||||
vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, c.drawIndexed.indexType);
|
||||
vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &c.drawIndexed.voffset);
|
||||
{
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets);
|
||||
vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, (VkIndexType)c.drawIndexed.indexType);
|
||||
VkDeviceSize voffset = c.drawIndexed.voffset;
|
||||
vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &voffset);
|
||||
vkCmdDrawIndexed(cmd, c.drawIndexed.count, c.drawIndexed.instances, 0, 0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case VKRRenderCommand::DRAW:
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.draw.pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets);
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets);
|
||||
if (c.draw.vbuffer) {
|
||||
vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset);
|
||||
}
|
||||
@@ -1793,6 +1803,8 @@ void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataForm
|
||||
}
|
||||
} else if (destFormat == Draw::DataFormat::D32F) {
|
||||
ConvertToD32F(pixels, (const uint8_t *)mappedData, pixelStride, width, width, height, srcFormat);
|
||||
} else if (destFormat == Draw::DataFormat::D16) {
|
||||
ConvertToD16(pixels, (const uint8_t *)mappedData, pixelStride, width, width, height, srcFormat);
|
||||
} else {
|
||||
// TODO: Maybe a depth conversion or something?
|
||||
ERROR_LOG(G3D, "CopyReadbackBuffer: Unknown format");
|
||||
|
||||
@@ -52,15 +52,17 @@ struct VkRenderData {
|
||||
union {
|
||||
struct {
|
||||
VkPipeline pipeline;
|
||||
VkPipelineLayout pipelineLayout;
|
||||
} pipeline;
|
||||
struct {
|
||||
VKRGraphicsPipeline *pipeline;
|
||||
VkPipelineLayout pipelineLayout;
|
||||
} graphics_pipeline;
|
||||
struct {
|
||||
VKRComputePipeline *pipeline;
|
||||
VkPipelineLayout pipelineLayout;
|
||||
} compute_pipeline;
|
||||
struct {
|
||||
VkPipelineLayout pipelineLayout;
|
||||
VkDescriptorSet ds;
|
||||
int numUboOffsets;
|
||||
uint32_t uboOffsets[3];
|
||||
@@ -70,17 +72,16 @@ struct VkRenderData {
|
||||
uint32_t offset;
|
||||
} draw;
|
||||
struct {
|
||||
VkPipelineLayout pipelineLayout;
|
||||
VkDescriptorSet ds;
|
||||
int numUboOffsets;
|
||||
uint32_t uboOffsets[3];
|
||||
VkBuffer vbuffer; // might need to increase at some point
|
||||
VkDeviceSize voffset;
|
||||
VkBuffer ibuffer;
|
||||
VkDeviceSize ioffset;
|
||||
uint32_t voffset;
|
||||
uint32_t ioffset;
|
||||
uint32_t count;
|
||||
int16_t instances;
|
||||
VkIndexType indexType;
|
||||
int16_t indexType;
|
||||
} drawIndexed;
|
||||
struct {
|
||||
uint32_t clearColor;
|
||||
@@ -103,7 +104,6 @@ struct VkRenderData {
|
||||
uint32_t color;
|
||||
} blendColor;
|
||||
struct {
|
||||
VkPipelineLayout pipelineLayout;
|
||||
VkShaderStageFlags stages;
|
||||
uint8_t offset;
|
||||
uint8_t size;
|
||||
@@ -134,8 +134,8 @@ enum class VKRRenderPassStoreAction : uint8_t {
|
||||
};
|
||||
|
||||
struct TransitionRequest {
|
||||
VkImageAspectFlags aspect; // COLOR or DEPTH
|
||||
VKRFramebuffer *fb;
|
||||
VkImageAspectFlags aspect; // COLOR or DEPTH
|
||||
VkImageLayout targetLayout;
|
||||
};
|
||||
|
||||
@@ -153,7 +153,7 @@ struct VKRStep {
|
||||
|
||||
VKRStepType stepType;
|
||||
std::vector<VkRenderData> commands;
|
||||
std::vector<TransitionRequest> preTransitions;
|
||||
TinySet<TransitionRequest, 4> preTransitions;
|
||||
TinySet<VKRFramebuffer *, 8> dependencies;
|
||||
const char *tag;
|
||||
union {
|
||||
|
||||
@@ -836,6 +836,7 @@ bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, VkIma
|
||||
} else {
|
||||
_assert_(false);
|
||||
}
|
||||
|
||||
// Need to call this after FlushSync so the pixels are guaranteed to be ready in CPU-accessible VRAM.
|
||||
queueRunner_.CopyReadbackBuffer(w, h, srcFormat, destFormat, pixelStride, pixels);
|
||||
return true;
|
||||
@@ -1188,7 +1189,7 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in
|
||||
// We're done.
|
||||
return aspectBit == VK_IMAGE_ASPECT_COLOR_BIT ? fb->color.imageView : fb->depth.depthSampleView;
|
||||
} else {
|
||||
curRenderStep_->preTransitions.push_back({ aspectBit, fb, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL });
|
||||
curRenderStep_->preTransitions.push_back({ fb, aspectBit, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL });
|
||||
return aspectBit == VK_IMAGE_ASPECT_COLOR_BIT ? fb->color.imageView : fb->depth.depthSampleView;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,29 +242,32 @@ public:
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
void BindPipeline(VkPipeline pipeline, PipelineFlags flags) {
|
||||
void BindPipeline(VkPipeline pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
|
||||
_dbg_assert_(pipeline != VK_NULL_HANDLE);
|
||||
VkRenderData data{ VKRRenderCommand::BIND_PIPELINE };
|
||||
data.pipeline.pipeline = pipeline;
|
||||
data.pipeline.pipelineLayout = pipelineLayout;
|
||||
curPipelineFlags_ |= flags;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags) {
|
||||
void BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
|
||||
_dbg_assert_(pipeline != nullptr);
|
||||
VkRenderData data{ VKRRenderCommand::BIND_GRAPHICS_PIPELINE };
|
||||
data.graphics_pipeline.pipeline = pipeline;
|
||||
data.graphics_pipeline.pipelineLayout = pipelineLayout;
|
||||
curPipelineFlags_ |= flags;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void BindPipeline(VKRComputePipeline *pipeline, PipelineFlags flags) {
|
||||
void BindPipeline(VKRComputePipeline *pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
|
||||
_dbg_assert_(pipeline != nullptr);
|
||||
VkRenderData data{ VKRRenderCommand::BIND_COMPUTE_PIPELINE };
|
||||
data.compute_pipeline.pipeline = pipeline;
|
||||
data.compute_pipeline.pipelineLayout = pipelineLayout;
|
||||
curPipelineFlags_ |= flags;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
@@ -351,7 +354,6 @@ public:
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
|
||||
_dbg_assert_(size + offset < 40);
|
||||
VkRenderData data{ VKRRenderCommand::PUSH_CONSTANTS };
|
||||
data.push.pipelineLayout = pipelineLayout;
|
||||
data.push.stages = stages;
|
||||
data.push.offset = offset;
|
||||
data.push.size = size;
|
||||
@@ -386,12 +388,11 @@ public:
|
||||
curRenderStep_->render.stencilStore = VKRRenderPassStoreAction::DONT_CARE;
|
||||
}
|
||||
|
||||
void Draw(VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count, int offset = 0) {
|
||||
void Draw(VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count, int offset = 0) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_);
|
||||
VkRenderData data{ VKRRenderCommand::DRAW };
|
||||
data.draw.count = count;
|
||||
data.draw.offset = offset;
|
||||
data.draw.pipelineLayout = layout;
|
||||
data.draw.ds = descSet;
|
||||
data.draw.vbuffer = vbuffer;
|
||||
data.draw.voffset = voffset;
|
||||
@@ -403,12 +404,11 @@ public:
|
||||
curRenderStep_->render.numDraws++;
|
||||
}
|
||||
|
||||
void DrawIndexed(VkPipelineLayout layout, VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances, VkIndexType indexType) {
|
||||
void DrawIndexed(VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances, VkIndexType indexType) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_);
|
||||
VkRenderData data{ VKRRenderCommand::DRAW_INDEXED };
|
||||
data.drawIndexed.count = count;
|
||||
data.drawIndexed.instances = numInstances;
|
||||
data.drawIndexed.pipelineLayout = layout;
|
||||
data.drawIndexed.ds = descSet;
|
||||
data.drawIndexed.vbuffer = vbuffer;
|
||||
data.drawIndexed.voffset = voffset;
|
||||
|
||||
@@ -1339,7 +1339,7 @@ void VKContext::Draw(int vertexCount, int offset) {
|
||||
|
||||
BindCompatiblePipeline();
|
||||
ApplyDynamicState();
|
||||
renderManager_.Draw(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount, offset);
|
||||
renderManager_.Draw(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount, offset);
|
||||
}
|
||||
|
||||
void VKContext::DrawIndexed(int vertexCount, int offset) {
|
||||
@@ -1359,7 +1359,7 @@ void VKContext::DrawIndexed(int vertexCount, int offset) {
|
||||
|
||||
BindCompatiblePipeline();
|
||||
ApplyDynamicState();
|
||||
renderManager_.DrawIndexed(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vulkanIbuf, (int)ibBindOffset + offset * sizeof(uint32_t), vertexCount, 1, VK_INDEX_TYPE_UINT16);
|
||||
renderManager_.DrawIndexed(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vulkanIbuf, (int)ibBindOffset + offset * sizeof(uint32_t), vertexCount, 1, VK_INDEX_TYPE_UINT16);
|
||||
}
|
||||
|
||||
void VKContext::DrawUP(const void *vdata, int vertexCount) {
|
||||
@@ -1375,15 +1375,15 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) {
|
||||
|
||||
BindCompatiblePipeline();
|
||||
ApplyDynamicState();
|
||||
renderManager_.Draw(pipelineLayout_, descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount);
|
||||
renderManager_.Draw(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount);
|
||||
}
|
||||
|
||||
void VKContext::BindCompatiblePipeline() {
|
||||
VkRenderPass renderPass = renderManager_.GetCompatibleRenderPass();
|
||||
if (renderPass == renderManager_.GetBackbufferRenderPass()) {
|
||||
renderManager_.BindPipeline(curPipeline_->backbufferPipeline, curPipeline_->flags);
|
||||
renderManager_.BindPipeline(curPipeline_->backbufferPipeline, curPipeline_->flags, pipelineLayout_);
|
||||
} else {
|
||||
renderManager_.BindPipeline(curPipeline_->framebufferPipeline, curPipeline_->flags);
|
||||
renderManager_.BindPipeline(curPipeline_->framebufferPipeline, curPipeline_->flags, pipelineLayout_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -623,6 +623,48 @@ void ConvertToD32F(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This is missing the conversion to the quarter-range we use if depth clamp is not available.
|
||||
// That conversion doesn't necessarily belong here in thin3d, though.
|
||||
void ConvertToD16(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format) {
|
||||
if (format == Draw::DataFormat::D32F) {
|
||||
const float *src32 = (const float *)src;
|
||||
uint16_t *dst16 = (uint16_t *)dst;
|
||||
if (src == dst) {
|
||||
return;
|
||||
} else {
|
||||
for (uint32_t y = 0; y < height; ++y) {
|
||||
for (uint32_t x = 0; x < width; ++x) {
|
||||
dst16[x] = (uint16_t)(src32[x] * 65535.0f);
|
||||
}
|
||||
src32 += srcStride;
|
||||
dst16 += dstStride;
|
||||
}
|
||||
}
|
||||
} else if (format == Draw::DataFormat::D16) {
|
||||
_assert_(src != dst);
|
||||
const uint16_t *src16 = (const uint16_t *)src;
|
||||
uint16_t *dst16 = (uint16_t *)dst;
|
||||
for (uint32_t y = 0; y < height; ++y) {
|
||||
memcpy(dst16, src16, width * 2);
|
||||
src16 += srcStride;
|
||||
dst16 += dstStride;
|
||||
}
|
||||
} else if (format == Draw::DataFormat::D24_S8) {
|
||||
_assert_(src != dst);
|
||||
const uint32_t *src32 = (const uint32_t *)src;
|
||||
uint16_t *dst16 = (uint16_t *)dst;
|
||||
for (uint32_t y = 0; y < height; ++y) {
|
||||
for (uint32_t x = 0; x < width; ++x) {
|
||||
dst16[x] = (src32[x] & 0x00FFFFFF) >> 8;
|
||||
}
|
||||
src32 += srcStride;
|
||||
dst16 += dstStride;
|
||||
}
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
const char *Bugs::GetBugName(uint32_t bug) {
|
||||
switch (bug) {
|
||||
case NO_DEPTH_CANNOT_DISCARD_STENCIL: return "NO_DEPTH_CANNOT_DISCARD_STENCIL";
|
||||
|
||||
@@ -44,15 +44,16 @@
|
||||
#include "Common/Buffer.h"
|
||||
#include "Common/StringUtils.h"
|
||||
|
||||
void truncate_cpy(char *dest, size_t destSize, const char *src) {
|
||||
size_t truncate_cpy(char *dest, size_t destSize, const char *src) {
|
||||
size_t len = strlen(src);
|
||||
if (len >= destSize - 1) {
|
||||
memcpy(dest, src, destSize - 1);
|
||||
dest[destSize - 1] = '\0';
|
||||
len = destSize - 1;
|
||||
} else {
|
||||
memcpy(dest, src, len);
|
||||
dest[len] = '\0';
|
||||
}
|
||||
dest[len] = '\0';
|
||||
return len;
|
||||
}
|
||||
|
||||
const char* safe_string(const char* s) {
|
||||
|
||||
@@ -78,10 +78,10 @@ std::string ReplaceAll(std::string input, const std::string& src, const std::str
|
||||
|
||||
void SkipSpace(const char **ptr);
|
||||
|
||||
void truncate_cpy(char *dest, size_t destSize, const char *src);
|
||||
size_t truncate_cpy(char *dest, size_t destSize, const char *src);
|
||||
template<size_t Count>
|
||||
inline void truncate_cpy(char(&out)[Count], const char *src) {
|
||||
truncate_cpy(out, Count, src);
|
||||
inline size_t truncate_cpy(char(&out)[Count], const char *src) {
|
||||
return truncate_cpy(out, Count, src);
|
||||
}
|
||||
|
||||
const char* safe_string(const char* s);
|
||||
|
||||
@@ -67,7 +67,6 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) {
|
||||
CheckSetting(iniFile, gameID, "RequireBufferedRendering", &flags_.RequireBufferedRendering);
|
||||
CheckSetting(iniFile, gameID, "RequireBlockTransfer", &flags_.RequireBlockTransfer);
|
||||
CheckSetting(iniFile, gameID, "RequireDefaultCPUClock", &flags_.RequireDefaultCPUClock);
|
||||
CheckSetting(iniFile, gameID, "DisableReadbacks", &flags_.DisableReadbacks);
|
||||
CheckSetting(iniFile, gameID, "DisableAccurateDepth", &flags_.DisableAccurateDepth);
|
||||
CheckSetting(iniFile, gameID, "MGS2AcidHack", &flags_.MGS2AcidHack);
|
||||
CheckSetting(iniFile, gameID, "SonicRivalsHack", &flags_.SonicRivalsHack);
|
||||
@@ -97,6 +96,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) {
|
||||
CheckSetting(iniFile, gameID, "AllowLargeFBTextureOffsets", &flags_.AllowLargeFBTextureOffsets);
|
||||
CheckSetting(iniFile, gameID, "AtracLoopHack", &flags_.AtracLoopHack);
|
||||
CheckSetting(iniFile, gameID, "DeswizzleDepth", &flags_.DeswizzleDepth);
|
||||
CheckSetting(iniFile, gameID, "SplitFramebufferMargin", &flags_.SplitFramebufferMargin);
|
||||
}
|
||||
|
||||
void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) {
|
||||
|
||||
@@ -57,7 +57,6 @@ struct CompatFlags {
|
||||
bool RequireBufferedRendering;
|
||||
bool RequireBlockTransfer;
|
||||
bool RequireDefaultCPUClock;
|
||||
bool DisableReadbacks;
|
||||
bool DisableAccurateDepth;
|
||||
bool MGS2AcidHack;
|
||||
bool SonicRivalsHack;
|
||||
@@ -87,6 +86,7 @@ struct CompatFlags {
|
||||
bool AllowLargeFBTextureOffsets;
|
||||
bool AtracLoopHack;
|
||||
bool DeswizzleDepth;
|
||||
bool SplitFramebufferMargin;
|
||||
};
|
||||
|
||||
class IniFile;
|
||||
|
||||
+1
-1
@@ -935,7 +935,7 @@ static ConfigSetting graphicsSettings[] = {
|
||||
ConfigSetting("ShaderChainRequires60FPS", &g_Config.bShaderChainRequires60FPS, false, true, true),
|
||||
|
||||
ReportedConfigSetting("MemBlockTransferGPU", &g_Config.bBlockTransferGPU, true, true, true),
|
||||
ReportedConfigSetting("DisableSlowFramebufEffects", &g_Config.bDisableSlowFramebufEffects, false, true, true),
|
||||
ReportedConfigSetting("DisableSlowFramebufEffects", &g_Config.bDisableShaderBlending, false, true, true),
|
||||
ReportedConfigSetting("FragmentTestCache", &g_Config.bFragmentTestCache, true, true, true),
|
||||
|
||||
ConfigSetting("GfxDebugOutput", &g_Config.bGfxDebugOutput, false, false, false),
|
||||
|
||||
+1
-1
@@ -232,7 +232,7 @@ public:
|
||||
float fGameListScrollPosition;
|
||||
int iBloomHack; //0 = off, 1 = safe, 2 = balanced, 3 = aggressive
|
||||
bool bBlockTransferGPU;
|
||||
bool bDisableSlowFramebufEffects;
|
||||
bool bDisableShaderBlending;
|
||||
bool bFragmentTestCache;
|
||||
int iSplineBezierQuality; // 0 = low , 1 = Intermediate , 2 = High
|
||||
bool bHardwareTessellation;
|
||||
|
||||
@@ -37,7 +37,8 @@ public:
|
||||
|
||||
bool Mark(uint32_t addr, uint32_t size, uint64_t ticks, uint32_t pc, bool allocated, const char *tag);
|
||||
bool Find(MemBlockFlags flags, uint32_t addr, uint32_t size, std::vector<MemBlockInfo> &results);
|
||||
bool FastFindWriteTag(MemBlockFlags flags, uint32_t addr, uint32_t size, std::string &result);
|
||||
// Note that the returned pointer gets invalidated as soon as Mark is called.
|
||||
const char *FastFindWriteTag(MemBlockFlags flags, uint32_t addr, uint32_t size);
|
||||
void Reset();
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
@@ -151,17 +152,16 @@ bool MemSlabMap::Find(MemBlockFlags flags, uint32_t addr, uint32_t size, std::ve
|
||||
return found;
|
||||
}
|
||||
|
||||
bool MemSlabMap::FastFindWriteTag(MemBlockFlags flags, uint32_t addr, uint32_t size, std::string &result) {
|
||||
const char *MemSlabMap::FastFindWriteTag(MemBlockFlags flags, uint32_t addr, uint32_t size) {
|
||||
uint32_t end = addr + size;
|
||||
Slab *slab = FindSlab(addr);
|
||||
while (slab != nullptr && slab->start < end) {
|
||||
if (slab->pc != 0 || slab->tag[0] != '\0') {
|
||||
result = slab->tag;
|
||||
return true;
|
||||
return slab->tag;
|
||||
}
|
||||
slab = slab->next;
|
||||
}
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MemSlabMap::Reset() {
|
||||
@@ -485,7 +485,7 @@ std::vector<MemBlockInfo> FindMemInfoByFlag(MemBlockFlags flags, uint32_t start,
|
||||
return results;
|
||||
}
|
||||
|
||||
static std::string FindWriteTagByFlag(MemBlockFlags flags, uint32_t start, uint32_t size) {
|
||||
static const char *FindWriteTagByFlag(MemBlockFlags flags, uint32_t start, uint32_t size) {
|
||||
start &= ~0xC0000000;
|
||||
|
||||
if (pendingNotifyMinAddr1 < start + size && pendingNotifyMaxAddr1 >= start)
|
||||
@@ -493,37 +493,51 @@ static std::string FindWriteTagByFlag(MemBlockFlags flags, uint32_t start, uint3
|
||||
if (pendingNotifyMinAddr2 < start + size && pendingNotifyMaxAddr2 >= start)
|
||||
FlushPendingMemInfo();
|
||||
|
||||
std::string tag;
|
||||
if (flags & MemBlockFlags::ALLOC) {
|
||||
if (allocMap.FastFindWriteTag(MemBlockFlags::ALLOC, start, size, tag))
|
||||
const char *tag = allocMap.FastFindWriteTag(MemBlockFlags::ALLOC, start, size);
|
||||
if (tag)
|
||||
return tag;
|
||||
}
|
||||
if (flags & MemBlockFlags::SUB_ALLOC) {
|
||||
if (suballocMap.FastFindWriteTag(MemBlockFlags::SUB_ALLOC, start, size, tag))
|
||||
const char *tag = suballocMap.FastFindWriteTag(MemBlockFlags::SUB_ALLOC, start, size);
|
||||
if (tag)
|
||||
return tag;
|
||||
}
|
||||
if (flags & MemBlockFlags::WRITE) {
|
||||
if (writeMap.FastFindWriteTag(MemBlockFlags::WRITE, start, size, tag))
|
||||
const char *tag = writeMap.FastFindWriteTag(MemBlockFlags::WRITE, start, size);
|
||||
if (tag)
|
||||
return tag;
|
||||
}
|
||||
if (flags & MemBlockFlags::TEXTURE) {
|
||||
if (textureMap.FastFindWriteTag(MemBlockFlags::TEXTURE, start, size, tag))
|
||||
const char *tag = textureMap.FastFindWriteTag(MemBlockFlags::TEXTURE, start, size);
|
||||
if (tag)
|
||||
return tag;
|
||||
}
|
||||
return "";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string GetMemWriteTagAt(uint32_t start, uint32_t size) {
|
||||
std::string tag = FindWriteTagByFlag(MemBlockFlags::WRITE, start, size);
|
||||
if (!tag.empty() && tag != "MemInit")
|
||||
return tag;
|
||||
|
||||
std::string GetMemWriteTagAt(const char *prefix, uint32_t start, uint32_t size) {
|
||||
const char *tag = FindWriteTagByFlag(MemBlockFlags::WRITE, start, size);
|
||||
if (tag && strcmp(tag, "MemInit") != 0)
|
||||
return std::string(prefix) + tag;
|
||||
// Fall back to alloc and texture, especially for VRAM. We prefer write above.
|
||||
tag = FindWriteTagByFlag(MemBlockFlags::ALLOC | MemBlockFlags::TEXTURE, start, size);
|
||||
if (!tag.empty())
|
||||
return tag;
|
||||
if (tag)
|
||||
return std::string(prefix) + tag;
|
||||
return StringFromFormat("%s%08x_size_%08x", prefix, start, size);
|
||||
}
|
||||
|
||||
return StringFromFormat("%08x_size_%08x", start, size);
|
||||
size_t FormatMemWriteTagAt(char *buf, size_t sz, const char *prefix, uint32_t start, uint32_t size) {
|
||||
const char *tag = FindWriteTagByFlag(MemBlockFlags::WRITE, start, size);
|
||||
if (tag && strcmp(tag, "MemInit") != 0) {
|
||||
return snprintf(buf, sz, "%s%s", prefix, tag);
|
||||
}
|
||||
// Fall back to alloc and texture, especially for VRAM. We prefer write above.
|
||||
tag = FindWriteTagByFlag(MemBlockFlags::ALLOC | MemBlockFlags::TEXTURE, start, size);
|
||||
if (tag) {
|
||||
return snprintf(buf, sz, "%s%s", prefix, tag);
|
||||
}
|
||||
return snprintf(buf, sz, "%s%08x_size_%08x", prefix, start, size);
|
||||
}
|
||||
|
||||
void MemBlockInfoInit() {
|
||||
|
||||
@@ -68,7 +68,9 @@ inline void NotifyMemInfo(MemBlockFlags flags, uint32_t start, uint32_t size, co
|
||||
std::vector<MemBlockInfo> FindMemInfo(uint32_t start, uint32_t size);
|
||||
std::vector<MemBlockInfo> FindMemInfoByFlag(MemBlockFlags flags, uint32_t start, uint32_t size);
|
||||
|
||||
std::string GetMemWriteTagAt(uint32_t start, uint32_t size);
|
||||
std::string GetMemWriteTagAt(const char *prefix, uint32_t start, uint32_t size);
|
||||
// Same as above but allocation-free.
|
||||
size_t FormatMemWriteTagAt(char *buf, size_t sz, const char *prefix, uint32_t start, uint32_t size);
|
||||
|
||||
void MemBlockInfoInit();
|
||||
void MemBlockInfoShutdown();
|
||||
|
||||
@@ -129,6 +129,6 @@ protected:
|
||||
|
||||
private:
|
||||
DialogStatus status = SCE_UTILITY_STATUS_NONE;
|
||||
UtilityDialogType dialogType_;
|
||||
UtilityDialogType dialogType_ = UtilityDialogType::NONE;
|
||||
bool volatileLocked_ = false;
|
||||
};
|
||||
@@ -285,9 +285,6 @@ struct SaveFileInfo
|
||||
|
||||
PPGeImage *texture = nullptr;
|
||||
|
||||
SaveFileInfo() {
|
||||
}
|
||||
|
||||
void DoState(PointerWrap &p);
|
||||
};
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ static int Replace_memcpy() {
|
||||
RETURN(destPtr);
|
||||
|
||||
if (MemBlockInfoDetailed(bytes)) {
|
||||
const std::string tag = "ReplaceMemcpy/" + GetMemWriteTagAt(srcPtr, bytes);
|
||||
const std::string tag = GetMemWriteTagAt("ReplaceMemcpy/", srcPtr, bytes);
|
||||
NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, tag.c_str(), tag.size());
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, tag.c_str(), tag.size());
|
||||
|
||||
@@ -211,7 +211,7 @@ static int Replace_memcpy_jak() {
|
||||
RETURN(destPtr);
|
||||
|
||||
if (MemBlockInfoDetailed(bytes)) {
|
||||
const std::string tag = "ReplaceMemcpy/" + GetMemWriteTagAt(srcPtr, bytes);
|
||||
const std::string tag = GetMemWriteTagAt("ReplaceMemcpy/", srcPtr, bytes);
|
||||
NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, tag.c_str(), tag.size());
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, tag.c_str(), tag.size());
|
||||
|
||||
@@ -249,7 +249,7 @@ static int Replace_memcpy16() {
|
||||
RETURN(destPtr);
|
||||
|
||||
if (MemBlockInfoDetailed(bytes)) {
|
||||
const std::string tag = "ReplaceMemcpy16/" + GetMemWriteTagAt(srcPtr, bytes);
|
||||
const std::string tag = GetMemWriteTagAt("ReplaceMemcpy16/", srcPtr, bytes);
|
||||
NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, tag.c_str(), tag.size());
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, tag.c_str(), tag.size());
|
||||
}
|
||||
@@ -290,7 +290,7 @@ static int Replace_memcpy_swizzled() {
|
||||
RETURN(0);
|
||||
|
||||
if (MemBlockInfoDetailed(pitch * h)) {
|
||||
const std::string tag = "ReplaceMemcpySwizzle/" + GetMemWriteTagAt(srcPtr, pitch * h);
|
||||
const std::string tag = GetMemWriteTagAt("ReplaceMemcpySwizzle/", srcPtr, pitch * h);
|
||||
NotifyMemInfo(MemBlockFlags::READ, srcPtr, pitch * h, tag.c_str(), tag.size());
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, destPtr, pitch * h, tag.c_str(), tag.size());
|
||||
}
|
||||
@@ -321,7 +321,7 @@ static int Replace_memmove() {
|
||||
RETURN(destPtr);
|
||||
|
||||
if (MemBlockInfoDetailed(bytes)) {
|
||||
const std::string tag = "ReplaceMemmove/" + GetMemWriteTagAt(srcPtr, bytes);
|
||||
const std::string tag = GetMemWriteTagAt("ReplaceMemmove/", srcPtr, bytes);
|
||||
NotifyMemInfo(MemBlockFlags::READ, srcPtr, bytes, tag.c_str(), tag.size());
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, destPtr, bytes, tag.c_str(), tag.size());
|
||||
}
|
||||
|
||||
+13
-11
@@ -64,8 +64,8 @@ int srcFrequency = 0;
|
||||
|
||||
const int hwSampleRate = 44100;
|
||||
|
||||
int hwBlockSize = 64;
|
||||
int hostAttemptBlockSize = 512;
|
||||
const int hwBlockSize = 64;
|
||||
const int hostAttemptBlockSize = 512;
|
||||
|
||||
static int audioIntervalCycles;
|
||||
static int audioHostIntervalCycles;
|
||||
@@ -82,6 +82,11 @@ static bool m_logAudio;
|
||||
static int chanQueueMaxSizeFactor;
|
||||
static int chanQueueMinSizeFactor;
|
||||
|
||||
// Accessor for libretro
|
||||
int __AudioGetHostAttemptBlockSize() {
|
||||
return hostAttemptBlockSize;
|
||||
}
|
||||
|
||||
static void hleAudioUpdate(u64 userdata, int cyclesLate) {
|
||||
// Schedule the next cycle first. __AudioUpdate() may consume cycles.
|
||||
CoreTiming::ScheduleEvent(audioIntervalCycles - cyclesLate, eventAudioUpdate, 0);
|
||||
@@ -110,8 +115,6 @@ void __AudioInit() {
|
||||
|
||||
chanQueueMaxSizeFactor = 2;
|
||||
chanQueueMinSizeFactor = 1;
|
||||
hwBlockSize = 64;
|
||||
hostAttemptBlockSize = 512;
|
||||
|
||||
__AudioCPUMHzChange();
|
||||
|
||||
@@ -338,7 +341,8 @@ void __AudioUpdate(bool resetRecording) {
|
||||
// to the CPU. Much better to throttle the frame rate on frame display and just throw away audio
|
||||
// if the buffer somehow gets full.
|
||||
bool firstChannel = true;
|
||||
std::vector<int16_t> srcBuffer;
|
||||
const int16_t srcBufferSize = hwBlockSize * 2;
|
||||
int16_t srcBuffer[srcBufferSize];
|
||||
|
||||
for (u32 i = 0; i < PSP_AUDIO_CHANNEL_MAX + 1; i++) {
|
||||
if (!chans[i].reserved)
|
||||
@@ -351,7 +355,7 @@ void __AudioUpdate(bool resetRecording) {
|
||||
}
|
||||
|
||||
bool needsResample = i == PSP_AUDIO_CHANNEL_SRC && srcFrequency != 0 && srcFrequency != mixFrequency;
|
||||
size_t sz = needsResample ? (hwBlockSize * 2 * srcFrequency) / mixFrequency : hwBlockSize * 2;
|
||||
size_t sz = needsResample ? (srcBufferSize * srcFrequency) / mixFrequency : srcBufferSize;
|
||||
if (sz > chanSampleQueues[i].size()) {
|
||||
ERROR_LOG(SCEAUDIO, "Channel %i buffer underrun at %i of %i", i, (int)chanSampleQueues[i].size() / 2, (int)sz / 2);
|
||||
}
|
||||
@@ -372,13 +376,11 @@ void __AudioUpdate(bool resetRecording) {
|
||||
return buf1[sz1 - 1];
|
||||
};
|
||||
|
||||
srcBuffer.resize(hwBlockSize * 2);
|
||||
|
||||
// TODO: This is terrible, since it's doing it by small chunk and discarding frac.
|
||||
const uint32_t ratio = (uint32_t)(65536.0 * srcFrequency / (double)mixFrequency);
|
||||
uint32_t frac = 0;
|
||||
size_t readIndex = 0;
|
||||
for (size_t outIndex = 0; readIndex < sz && outIndex < srcBuffer.size(); outIndex += 2) {
|
||||
for (size_t outIndex = 0; readIndex < sz && outIndex < srcBufferSize; outIndex += 2) {
|
||||
size_t readIndex2 = readIndex + 2;
|
||||
int16_t l1 = read(readIndex);
|
||||
int16_t r1 = read(readIndex + 1);
|
||||
@@ -393,8 +395,8 @@ void __AudioUpdate(bool resetRecording) {
|
||||
frac &= 0xffff;
|
||||
}
|
||||
|
||||
buf1 = srcBuffer.data();
|
||||
sz1 = srcBuffer.size();
|
||||
buf1 = srcBuffer;
|
||||
sz1 = srcBufferSize;
|
||||
buf2 = nullptr;
|
||||
sz2 = 0;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ int __AudioMix(short *outstereo, int numSamples, int sampleRate);
|
||||
void __AudioGetDebugStats(char *buf, size_t bufSize);
|
||||
void __PushExternalAudio(const s32 *audio, int numSamples); // Should not be used in-game, only at the menu!
|
||||
|
||||
int __AudioGetHostAttemptBlockSize();
|
||||
|
||||
// Audio Dumping stuff
|
||||
void __StartLogAudio(const Path &filename);
|
||||
void __StopLogAudio();
|
||||
|
||||
@@ -1268,7 +1268,7 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3
|
||||
if (outbufPtr != 0) {
|
||||
u32 outBytes = numSamples * atrac->outputChannels_ * sizeof(s16);
|
||||
if (packetAddr != 0 && MemBlockInfoDetailed()) {
|
||||
const std::string tag = "AtracDecode/" + GetMemWriteTagAt(packetAddr, packetSize);
|
||||
const std::string tag = GetMemWriteTagAt("AtracDecode/", packetAddr, packetSize);
|
||||
NotifyMemInfo(MemBlockFlags::READ, packetAddr, packetSize, tag.c_str(), tag.size());
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, tag.c_str(), tag.size());
|
||||
} else {
|
||||
|
||||
@@ -59,7 +59,7 @@ static int CommonDecompress(int windowBits, u32 OutBuffer, int OutBufferLength,
|
||||
}
|
||||
|
||||
if (MemBlockInfoDetailed(stream.total_in, stream.total_out)) {
|
||||
const std::string tag = "sceDeflt/" + GetMemWriteTagAt(InBuffer, stream.total_in);
|
||||
const std::string tag = GetMemWriteTagAt("sceDeflt/", InBuffer, stream.total_in);
|
||||
NotifyMemInfo(MemBlockFlags::READ, InBuffer, stream.total_in, tag.c_str(), tag.size());
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, OutBuffer, stream.total_out, tag.c_str(), tag.size());
|
||||
}
|
||||
|
||||
@@ -823,7 +823,10 @@ u32 sceDisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync)
|
||||
|
||||
s64 delayCycles = 0;
|
||||
// Don't count transitions between display off and display on.
|
||||
if (topaddr != 0 && topaddr != framebuf.topaddr && framebuf.topaddr != 0 && PSP_CoreParameter().compat.flags().ForceMax60FPS) {
|
||||
if (topaddr != 0 &&
|
||||
(topaddr != framebuf.topaddr || PSP_CoreParameter().compat.flags().SplitFramebufferMargin) &&
|
||||
framebuf.topaddr != 0 &&
|
||||
PSP_CoreParameter().compat.flags().ForceMax60FPS) {
|
||||
// sceDisplaySetFramebuf() isn't supposed to delay threads at all. This is a hack.
|
||||
// So let's only delay when it's more than 1ms.
|
||||
const s64 FLIP_DELAY_CYCLES_MIN = usToCycles(1000);
|
||||
|
||||
@@ -52,7 +52,7 @@ static int __DmacMemcpy(u32 dst, u32 src, u32 size) {
|
||||
if (!skip) {
|
||||
currentMIPS->InvalidateICache(src, size);
|
||||
if (MemBlockInfoDetailed(size)) {
|
||||
const std::string tag = "DmacMemcpy/" + GetMemWriteTagAt(src, size);
|
||||
const std::string tag = GetMemWriteTagAt("DmacMemcpy/", src, size);
|
||||
Memory::Memcpy(dst, src, size, tag.c_str(), tag.size());
|
||||
} else {
|
||||
Memory::Memcpy(dst, src, size, "DmacMemcpy");
|
||||
|
||||
@@ -656,7 +656,7 @@ static u32 sceKernelMemcpy(u32 dst, u32 src, u32 size)
|
||||
}
|
||||
|
||||
if (MemBlockInfoDetailed(size)) {
|
||||
const std::string tag = "KernelMemcpy/" + GetMemWriteTagAt(src, size);
|
||||
const std::string tag = GetMemWriteTagAt("KernelMemcpy/", src, size);
|
||||
NotifyMemInfo(MemBlockFlags::READ, src, size, tag.c_str(), tag.size());
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, dst, size, tag.c_str(), tag.size());
|
||||
}
|
||||
@@ -691,7 +691,7 @@ static u32 sysclib_memcpy(u32 dst, u32 src, u32 size) {
|
||||
memcpy(Memory::GetPointerWriteUnchecked(dst), Memory::GetPointerUnchecked(src), size);
|
||||
}
|
||||
if (MemBlockInfoDetailed(size)) {
|
||||
const std::string tag = "KernelMemcpy/" + GetMemWriteTagAt(src, size);
|
||||
const std::string tag = GetMemWriteTagAt("KernelMemcpy/", src, size);
|
||||
NotifyMemInfo(MemBlockFlags::READ, src, size, tag.c_str(), tag.size());
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, dst, size, tag.c_str(), tag.size());
|
||||
}
|
||||
@@ -794,7 +794,7 @@ static u32 sysclib_memmove(u32 dst, u32 src, u32 size) {
|
||||
memmove(Memory::GetPointerWriteUnchecked(dst), Memory::GetPointerUnchecked(src), size);
|
||||
}
|
||||
if (MemBlockInfoDetailed(size)) {
|
||||
const std::string tag = "KernelMemmove/" + GetMemWriteTagAt(src, size);
|
||||
const std::string tag = GetMemWriteTagAt("KernelMemmove/", src, size);
|
||||
NotifyMemInfo(MemBlockFlags::READ, src, size, tag.c_str(), tag.size());
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, dst, size, tag.c_str(), tag.size());
|
||||
}
|
||||
|
||||
@@ -657,8 +657,10 @@ int sceKernelUnlockMutex(SceUID id, int count)
|
||||
|
||||
if (mutex->nm.lockLevel == 0)
|
||||
{
|
||||
if (__KernelUnlockMutex(mutex, error))
|
||||
if (__KernelUnlockMutex(mutex, error)) {
|
||||
hleReSchedule("mutex unlocked");
|
||||
return hleDelayResult(0, "unlock", 150);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Core/Debugger/MemBlockInfo.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
@@ -70,9 +71,9 @@ inline void Memcpy(const u32 to_address, const u32 from_address, const u32 len,
|
||||
if (MemBlockInfoDetailed(len)) {
|
||||
char tagData[128];
|
||||
if (!tag) {
|
||||
const std::string srcTag = GetMemWriteTagAt(from_address, len);
|
||||
const std::string srcTag = GetMemWriteTagAt("Memcpy/", from_address, len);
|
||||
tag = tagData;
|
||||
tagLen = snprintf(tagData, sizeof(tagData), "Memcpy/%s", srcTag.c_str());
|
||||
tagLen = truncate_cpy(tagData, srcTag.c_str());
|
||||
}
|
||||
NotifyMemInfo(MemBlockFlags::READ, from_address, len, tag, tagLen);
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, to_address, len, tag, tagLen);
|
||||
|
||||
@@ -701,12 +701,11 @@ void DrawEngineCommon::SubmitPrim(const void *verts, const void *inds, GEPrimiti
|
||||
DeferredDrawCall &dc = drawCalls[numDrawCalls];
|
||||
dc.verts = verts;
|
||||
dc.inds = inds;
|
||||
dc.vertexCount = vertexCount;
|
||||
dc.indexType = (vertTypeID & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT;
|
||||
dc.prim = prim;
|
||||
dc.vertexCount = vertexCount;
|
||||
dc.uvScale = gstate_c.uv;
|
||||
dc.cullMode = cullMode;
|
||||
|
||||
dc.uvScale = gstate_c.uv;
|
||||
if (inds) {
|
||||
GetIndexBounds(inds, vertexCount, vertTypeID, &dc.indexLowerBound, &dc.indexUpperBound);
|
||||
} else {
|
||||
|
||||
@@ -165,10 +165,10 @@ protected:
|
||||
u32 vertexCount;
|
||||
u8 indexType;
|
||||
s8 prim;
|
||||
u8 cullMode;
|
||||
u16 indexLowerBound;
|
||||
u16 indexUpperBound;
|
||||
UVScale uvScale;
|
||||
int cullMode;
|
||||
};
|
||||
|
||||
enum { MAX_DEFERRED_DRAW_CALLS = 128 };
|
||||
|
||||
@@ -132,11 +132,6 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
|
||||
return false;
|
||||
}
|
||||
|
||||
if (readFramebuffer && compat.shaderLanguage == HLSL_D3D9) {
|
||||
*errorString = "Framebuffer read not yet supported in HLSL D3D9";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (compat.shaderLanguage == ShaderLanguage::GLSL_VULKAN) {
|
||||
if (useDiscardStencilBugWorkaround && !gstate_c.Supports(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT)) {
|
||||
WRITE(p, "layout (depth_unchanged) out float gl_FragDepth;\n");
|
||||
@@ -227,7 +222,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
|
||||
}
|
||||
if (readFramebufferTex) {
|
||||
// No sampler required, we Load
|
||||
WRITE(p, "Texture2D<vec4> fboTex : register(t1);\n");
|
||||
WRITE(p, "Texture2D<vec4> fbotex : register(t1);\n");
|
||||
}
|
||||
WRITE(p, "cbuffer base : register(b0) {\n%s};\n", ub_baseStr);
|
||||
|
||||
@@ -264,8 +259,12 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
|
||||
if (enableFog) {
|
||||
WRITE(p, " float v_fogdepth: TEXCOORD1;\n");
|
||||
}
|
||||
if (compat.shaderLanguage == HLSL_D3D11 && needFragCoord) {
|
||||
WRITE(p, " vec4 pixelPos : SV_POSITION;\n");
|
||||
if (needFragCoord) {
|
||||
if (compat.shaderLanguage == HLSL_D3D11) {
|
||||
WRITE(p, " vec4 pixelPos : SV_POSITION;\n");
|
||||
} else if (compat.shaderLanguage == HLSL_D3D9) {
|
||||
WRITE(p, " vec4 pixelPos : VPOS;\n"); // VPOS is only supported for Shader Model 3.0, but we can probably forget about D3D9 SM2.0 at this point...
|
||||
}
|
||||
}
|
||||
WRITE(p, "};\n");
|
||||
|
||||
@@ -457,7 +456,9 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
|
||||
} else if (compat.shaderLanguage == HLSL_D3D9) {
|
||||
WRITE(p, "PS_OUT main( PS_IN In ) {\n");
|
||||
WRITE(p, " PS_OUT outfragment;\n");
|
||||
WRITE(p, " vec4 target;\n");
|
||||
if (needFragCoord) {
|
||||
WRITE(p, " vec4 gl_FragCoord = In.pixelPos;\n");
|
||||
}
|
||||
} else {
|
||||
WRITE(p, "void main() {\n");
|
||||
}
|
||||
@@ -477,7 +478,9 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
|
||||
// Two things read from the old framebuffer - shader replacement blending and bit-level masking.
|
||||
if (readFramebuffer) {
|
||||
if (compat.shaderLanguage == HLSL_D3D11) {
|
||||
WRITE(p, " vec4 destColor = fboTex.Load(int3((int)gl_FragCoord.x, (int)gl_FragCoord.y, 0));\n");
|
||||
WRITE(p, " vec4 destColor = fbotex.Load(int3((int)gl_FragCoord.x, (int)gl_FragCoord.y, 0));\n");
|
||||
} else if (compat.shaderLanguage == HLSL_D3D9) {
|
||||
WRITE(p, " vec4 destColor = tex2D(fbotex, gl_FragCoord.xy * u_fbotexSize.xy);\n", compat.texture);
|
||||
} else if (gstate_c.Supports(GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH)) {
|
||||
// If we have EXT_shader_framebuffer_fetch / ARM_shader_framebuffer_fetch, we skip the blit.
|
||||
// We can just read the prev value more directly.
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "Common/GPU/thin3d.h"
|
||||
#include "Common/GPU/OpenGL/GLFeatures.h"
|
||||
#include "Common/Data/Collections/TinySet.h"
|
||||
#include "Common/Data/Convert/ColorConv.h"
|
||||
#include "Common/Data/Text/I18n.h"
|
||||
#include "Common/Math/lin/matrix4x4.h"
|
||||
@@ -126,13 +127,19 @@ VirtualFramebuffer *FramebufferManagerCommon::GetVFBAt(u32 addr) const {
|
||||
}
|
||||
|
||||
VirtualFramebuffer *FramebufferManagerCommon::GetExactVFB(u32 addr, int stride, GEBufferFormat format) const {
|
||||
VirtualFramebuffer *newest = nullptr;
|
||||
for (auto vfb : vfbs_) {
|
||||
if (vfb->fb_address == addr && vfb->fb_stride == stride && vfb->fb_format == format) {
|
||||
// There'll only be one exact match, we don't allow duplicates with these conditions.
|
||||
return vfb;
|
||||
if (newest) {
|
||||
if (vfb->colorBindSeq > newest->colorBindSeq) {
|
||||
newest = vfb;
|
||||
}
|
||||
} else {
|
||||
newest = vfb;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
return newest;
|
||||
}
|
||||
|
||||
VirtualFramebuffer *FramebufferManagerCommon::ResolveVFB(u32 addr, int stride, GEBufferFormat format) {
|
||||
@@ -167,6 +174,7 @@ u32 FramebufferManagerCommon::ColorBufferByteSize(const VirtualFramebuffer *vfb)
|
||||
}
|
||||
|
||||
bool FramebufferManagerCommon::ShouldDownloadFramebuffer(const VirtualFramebuffer *vfb) const {
|
||||
// Dangan Ronpa hack
|
||||
return PSP_CoreParameter().compat.flags().Force04154000Download && vfb->fb_address == 0x04154000;
|
||||
}
|
||||
|
||||
@@ -326,15 +334,20 @@ void GetFramebufferHeuristicInputs(FramebufferHeuristicParams *params, const GPU
|
||||
params->viewportHeight = (int)(fabsf(vpy) * 2.0f);
|
||||
params->regionWidth = gstate.getRegionX2() + 1;
|
||||
params->regionHeight = gstate.getRegionY2() + 1;
|
||||
params->scissorWidth = gstate.getScissorX2() + 1;
|
||||
params->scissorHeight = gstate.getScissorY2() + 1;
|
||||
|
||||
params->scissorLeft = gstate.getScissorX1();
|
||||
params->scissorTop = gstate.getScissorY1();
|
||||
params->scissorRight = gstate.getScissorX2() + 1;
|
||||
params->scissorBottom = gstate.getScissorY2() + 1;
|
||||
|
||||
if (gstate.getRegionRateX() != 0x100 || gstate.getRegionRateY() != 0x100) {
|
||||
WARN_LOG_REPORT_ONCE(regionRate, G3D, "Drawing region rate add non-zero: %04x, %04x of %04x, %04x", gstate.getRegionRateX(), gstate.getRegionRateY(), gstate.getRegionX2(), gstate.getRegionY2());
|
||||
}
|
||||
}
|
||||
|
||||
VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const FramebufferHeuristicParams ¶ms, u32 skipDrawReason) {
|
||||
static void ApplyKillzoneFramebufferSplit(FramebufferHeuristicParams *params, int *drawing_width);
|
||||
|
||||
VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(FramebufferHeuristicParams ¶ms, u32 skipDrawReason) {
|
||||
gstate_c.Clean(DIRTY_FRAMEBUF);
|
||||
|
||||
// Collect all parameters. This whole function has really become a cesspool of heuristics...
|
||||
@@ -343,9 +356,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
|
||||
// As there are no clear "framebuffer width" and "framebuffer height" registers,
|
||||
// we need to infer the size of the current framebuffer somehow.
|
||||
int drawing_width, drawing_height;
|
||||
EstimateDrawingSize(params.fb_address, std::max(params.fb_stride, (u16)4), params.fb_format, params.viewportWidth, params.viewportHeight, params.regionWidth, params.regionHeight, params.scissorWidth, params.scissorHeight, drawing_width, drawing_height);
|
||||
|
||||
gstate_c.SetCurRTOffset(0, 0);
|
||||
EstimateDrawingSize(params.fb_address, std::max(params.fb_stride, (u16)4), params.fb_format, params.viewportWidth, params.viewportHeight, params.regionWidth, params.regionHeight, params.scissorRight, params.scissorBottom, drawing_width, drawing_height);
|
||||
|
||||
if (params.fb_address == params.z_address) {
|
||||
// Most likely Z will not be used in this pass, as that would wreak havoc (undefined behavior for sure)
|
||||
@@ -353,6 +364,13 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
|
||||
WARN_LOG_ONCE(color_equal_z, G3D, "Framebuffer bound with color addr == z addr, likely will not use Z in this pass: %08x", params.fb_address);
|
||||
}
|
||||
|
||||
// Compatibility hack for Killzone, see issue #6207.
|
||||
if (PSP_CoreParameter().compat.flags().SplitFramebufferMargin && params.fb_format == GE_FORMAT_8888) {
|
||||
ApplyKillzoneFramebufferSplit(¶ms, &drawing_width);
|
||||
} else {
|
||||
gstate_c.SetCurRTOffset(0, 0);
|
||||
}
|
||||
|
||||
// Find a matching framebuffer.
|
||||
VirtualFramebuffer *vfb = nullptr;
|
||||
for (auto v : vfbs_) {
|
||||
@@ -378,7 +396,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
|
||||
vfb->height = drawing_height;
|
||||
}
|
||||
break;
|
||||
} else if (v->fb_stride == params.fb_stride && v->fb_format == params.fb_format) {
|
||||
} else if (v->fb_stride == params.fb_stride && v->fb_format == params.fb_format && !PSP_CoreParameter().compat.flags().SplitFramebufferMargin) {
|
||||
u32 v_fb_first_line_end_ptr = v->fb_address + v->fb_stride * bpp;
|
||||
u32 v_fb_end_ptr = v->fb_address + v->fb_stride * v->height * bpp;
|
||||
|
||||
@@ -477,7 +495,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame
|
||||
currentRenderVfb_ = vfb;
|
||||
|
||||
// Assume that if we're clearing right when switching to a new framebuffer, we don't need to upload.
|
||||
if (useBufferedRendering_ && !g_Config.bDisableSlowFramebufEffects && params.isDrawing) {
|
||||
if (useBufferedRendering_ && params.isDrawing) {
|
||||
gpu->PerformMemoryUpload(params.fb_address, byteSize);
|
||||
// Alpha was already done by PerformMemoryUpload.
|
||||
PerformStencilUpload(params.fb_address, byteSize, StencilUpload::STENCIL_IS_ZERO | StencilUpload::IGNORE_ALPHA);
|
||||
@@ -659,7 +677,8 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra
|
||||
// This will result in reinterpret later, if both formats are 16-bit.
|
||||
sources.push_back(CopySource{ src, RASTER_COLOR, 0, 0 });
|
||||
} else {
|
||||
// Happens in Prince of Persia - Revelations. Ignoring.
|
||||
// This shouldn't happen anymore. I think when it happened last, we still had
|
||||
// lax stride checking when video was incoming, and a resize happened causing a duplicate.
|
||||
}
|
||||
} else if (src->fb_stride == dst->fb_stride && src->fb_format == dst->fb_format) {
|
||||
u32 bytesPerPixel = BufferFormatBytesPerPixel(src->fb_format);
|
||||
@@ -886,7 +905,6 @@ void FramebufferManagerCommon::NotifyRenderFramebufferCreated(VirtualFramebuffer
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::NotifyRenderFramebufferUpdated(VirtualFramebuffer *vfb) {
|
||||
// ugly...
|
||||
if (gstate_c.curRTWidth != vfb->width || gstate_c.curRTHeight != vfb->height) {
|
||||
gstate_c.Dirty(DIRTY_PROJTHROUGHMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE);
|
||||
}
|
||||
@@ -898,11 +916,12 @@ void FramebufferManagerCommon::NotifyRenderFramebufferUpdated(VirtualFramebuffer
|
||||
|
||||
void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffer *prevVfb, VirtualFramebuffer *vfb, bool isClearingDepth) {
|
||||
if (ShouldDownloadFramebuffer(vfb) && !vfb->memoryUpdated) {
|
||||
ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height);
|
||||
ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height, RASTER_COLOR);
|
||||
vfb->usageFlags = (vfb->usageFlags | FB_USAGE_DOWNLOAD | FB_USAGE_FIRST_FRAME_SAVED) & ~FB_USAGE_DOWNLOAD_CLEAR;
|
||||
} else {
|
||||
DownloadFramebufferOnSwitch(prevVfb);
|
||||
}
|
||||
|
||||
textureCache_->ForgetLastTexture();
|
||||
shaderManager_->DirtyLastShader();
|
||||
|
||||
@@ -959,41 +978,41 @@ void FramebufferManagerCommon::NotifyVideoUpload(u32 addr, int size, int stride,
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size, bool safe) {
|
||||
void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size) {
|
||||
// Take off the uncached flag from the address. Not to be confused with the start of VRAM.
|
||||
addr &= 0x3FFFFFFF;
|
||||
// TODO: Could go through all FBOs, but probably not important?
|
||||
// TODO: Could also check for inner changes, but video is most important.
|
||||
// TODO: This shouldn't care if it's a display framebuf or not, should work exactly the same.
|
||||
bool isDisplayBuf = addr == DisplayFramebufAddr() || addr == PrevDisplayFramebufAddr();
|
||||
if (isDisplayBuf || safe) {
|
||||
// TODO: Deleting the FBO is a heavy hammer solution, so let's only do it if it'd help.
|
||||
if (!Memory::IsValidAddress(displayFramebufPtr_))
|
||||
return;
|
||||
// TODO: Deleting the FBO is a heavy hammer solution, so let's only do it if it'd help.
|
||||
if (!Memory::IsValidAddress(displayFramebufPtr_))
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < vfbs_.size(); ++i) {
|
||||
VirtualFramebuffer *vfb = vfbs_[i];
|
||||
if (vfb->fb_address == addr) {
|
||||
FlushBeforeCopy();
|
||||
for (size_t i = 0; i < vfbs_.size(); ++i) {
|
||||
VirtualFramebuffer *vfb = vfbs_[i];
|
||||
if (vfb->fb_address == addr) {
|
||||
FlushBeforeCopy();
|
||||
|
||||
if (useBufferedRendering_ && vfb->fbo) {
|
||||
GEBufferFormat fmt = vfb->fb_format;
|
||||
if (vfb->last_frame_render + 1 < gpuStats.numFlips && isDisplayBuf) {
|
||||
// If we're not rendering to it, format may be wrong. Use displayFormat_ instead.
|
||||
fmt = displayFormat_;
|
||||
}
|
||||
DrawPixels(vfb, 0, 0, Memory::GetPointer(addr), fmt, vfb->fb_stride, vfb->width, vfb->height);
|
||||
SetColorUpdated(vfb, gstate_c.skipDrawReason);
|
||||
} else {
|
||||
INFO_LOG(FRAMEBUF, "Invalidating FBO for %08x (%i x %i x %i)", vfb->fb_address, vfb->width, vfb->height, vfb->fb_format);
|
||||
DestroyFramebuf(vfb);
|
||||
vfbs_.erase(vfbs_.begin() + i--);
|
||||
if (useBufferedRendering_ && vfb->fbo) {
|
||||
GEBufferFormat fmt = vfb->fb_format;
|
||||
if (vfb->last_frame_render + 1 < gpuStats.numFlips && isDisplayBuf) {
|
||||
// If we're not rendering to it, format may be wrong. Use displayFormat_ instead.
|
||||
// TODO: This doesn't seem quite right anymore.
|
||||
fmt = displayFormat_;
|
||||
}
|
||||
DrawPixels(vfb, 0, 0, Memory::GetPointer(addr), fmt, vfb->fb_stride, vfb->width, vfb->height);
|
||||
SetColorUpdated(vfb, gstate_c.skipDrawReason);
|
||||
} else {
|
||||
INFO_LOG(FRAMEBUF, "Invalidating FBO for %08x (%dx%d %s)", vfb->fb_address, vfb->width, vfb->height, GeBufferFormatToString(vfb->fb_format));
|
||||
DestroyFramebuf(vfb);
|
||||
vfbs_.erase(vfbs_.begin() + i--);
|
||||
}
|
||||
}
|
||||
|
||||
RebindFramebuffer("RebindFramebuffer - UpdateFromMemory");
|
||||
}
|
||||
|
||||
RebindFramebuffer("RebindFramebuffer - UpdateFromMemory");
|
||||
|
||||
// TODO: Necessary?
|
||||
gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE);
|
||||
}
|
||||
@@ -1106,7 +1125,7 @@ void FramebufferManagerCommon::CopyFramebufferForColorTexture(VirtualFramebuffer
|
||||
}
|
||||
|
||||
if (x < src->drawnWidth && y < src->drawnHeight && w > 0 && h > 0) {
|
||||
BlitFramebuffer(dst, x, y, src, x, y, w, h, 0, "Blit_CopyFramebufferForColorTexture");
|
||||
BlitFramebuffer(dst, x, y, src, x, y, w, h, 0, RASTER_COLOR, "Blit_CopyFramebufferForColorTexture");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1215,8 +1234,8 @@ void FramebufferManagerCommon::DownloadFramebufferOnSwitch(VirtualFramebuffer *v
|
||||
// Some games will draw to some memory once, and use it as a render-to-texture later.
|
||||
// To support this, we save the first frame to memory when we have a safe w/h.
|
||||
// Saving each frame would be slow.
|
||||
if (!g_Config.bDisableSlowFramebufEffects && !PSP_CoreParameter().compat.flags().DisableFirstFrameReadback) {
|
||||
ReadFramebufferToMemory(vfb, 0, 0, vfb->safeWidth, vfb->safeHeight);
|
||||
if (g_Config.bBlockTransferGPU && !PSP_CoreParameter().compat.flags().DisableFirstFrameReadback) {
|
||||
ReadFramebufferToMemory(vfb, 0, 0, vfb->safeWidth, vfb->safeHeight, RASTER_COLOR);
|
||||
vfb->usageFlags = (vfb->usageFlags | FB_USAGE_DOWNLOAD | FB_USAGE_FIRST_FRAME_SAVED) & ~FB_USAGE_DOWNLOAD_CLEAR;
|
||||
vfb->safeWidth = 0;
|
||||
vfb->safeHeight = 0;
|
||||
@@ -1369,7 +1388,7 @@ void FramebufferManagerCommon::DecimateFBOs() {
|
||||
int age = frameLastFramebufUsed_ - std::max(vfb->last_frame_render, vfb->last_frame_used);
|
||||
|
||||
if (ShouldDownloadFramebuffer(vfb) && age == 0 && !vfb->memoryUpdated) {
|
||||
ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height);
|
||||
ReadFramebufferToMemory(vfb, 0, 0, vfb->width, vfb->height, RASTER_COLOR);
|
||||
vfb->usageFlags = (vfb->usageFlags | FB_USAGE_DOWNLOAD | FB_USAGE_FIRST_FRAME_SAVED) & ~FB_USAGE_DOWNLOAD_CLEAR;
|
||||
}
|
||||
|
||||
@@ -1502,9 +1521,8 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w,
|
||||
if (vfb->fbo) {
|
||||
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "ResizeFramebufFBO");
|
||||
if (!skipCopy) {
|
||||
// TODO: In this case, it'll nearly always be better to draw the old framebuffer to the new one than to do an actual blit.
|
||||
// Usually hardly a performance issue though.
|
||||
BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min((u16)oldWidth, std::min(vfb->bufferWidth, vfb->width)), std::min((u16)oldHeight, std::min(vfb->height, vfb->bufferHeight)), 0, "Blit_ResizeFramebufFBO");
|
||||
BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min((u16)oldWidth, std::min(vfb->bufferWidth, vfb->width)), std::min((u16)oldHeight, std::min(vfb->height, vfb->bufferHeight)), 0, RASTER_COLOR, "Blit_ResizeFramebufFBO");
|
||||
// Depth copying is handled by deferred copies later.
|
||||
}
|
||||
}
|
||||
fbosToDelete_.push_back(old.fbo);
|
||||
@@ -1608,7 +1626,7 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size,
|
||||
} else {
|
||||
WARN_LOG_ONCE(dstnotsrccpy, G3D, "Inter-buffer memcpy %08x -> %08x (size: %x)", src, dst, size);
|
||||
// Just do the blit!
|
||||
BlitFramebuffer(dstBuffer, 0, dstY, srcBuffer, 0, srcY, srcBuffer->width, srcH, 0, "Blit_InterBufferMemcpy");
|
||||
BlitFramebuffer(dstBuffer, 0, dstY, srcBuffer, 0, srcY, srcBuffer->width, srcH, 0, RASTER_COLOR, "Blit_InterBufferMemcpy");
|
||||
SetColorUpdated(dstBuffer, skipDrawReason);
|
||||
RebindFramebuffer("RebindFramebuffer - Inter-buffer memcpy");
|
||||
}
|
||||
@@ -1630,8 +1648,8 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size,
|
||||
FlushBeforeCopy();
|
||||
if (srcH == 0 || srcY + srcH > srcBuffer->bufferHeight) {
|
||||
WARN_LOG_ONCE(btdcpyheight, G3D, "Memcpy fbo download %08x -> %08x skipped, %d+%d is taller than %d", src, dst, srcY, srcH, srcBuffer->bufferHeight);
|
||||
} else if (g_Config.bBlockTransferGPU && !srcBuffer->memoryUpdated && !PSP_CoreParameter().compat.flags().DisableReadbacks) {
|
||||
ReadFramebufferToMemory(srcBuffer, 0, srcY, srcBuffer->width, srcH);
|
||||
} else if (g_Config.bBlockTransferGPU && !srcBuffer->memoryUpdated) {
|
||||
ReadFramebufferToMemory(srcBuffer, 0, srcY, srcBuffer->width, srcH, RASTER_COLOR);
|
||||
srcBuffer->usageFlags = (srcBuffer->usageFlags | FB_USAGE_DOWNLOAD) & ~FB_USAGE_DOWNLOAD_CLEAR;
|
||||
}
|
||||
return false;
|
||||
@@ -1660,7 +1678,7 @@ bool FramebufferManagerCommon::FindTransferFramebuffer(u32 basePtr, int stride_p
|
||||
int x_bytes = x_pixels * bpp;
|
||||
int w_bytes = w_pixels * bpp;
|
||||
|
||||
std::vector<BlockTransferRect> candidates;
|
||||
TinySet<BlockTransferRect, 4> candidates;
|
||||
|
||||
// We work entirely in bytes when we do the matching, because games don't consistently use bpps that match
|
||||
// that of their buffers. Then after matching we try to map the copy to the simplest operation that does
|
||||
@@ -1735,20 +1753,27 @@ bool FramebufferManagerCommon::FindTransferFramebuffer(u32 basePtr, int stride_p
|
||||
candidates.push_back(candidate);
|
||||
}
|
||||
|
||||
const BlockTransferRect *best = nullptr;
|
||||
// Sort candidates by just recency for now, we might add other.
|
||||
std::sort(candidates.begin(), candidates.end());
|
||||
for (size_t i = 0; i < candidates.size(); i++) {
|
||||
const BlockTransferRect *candidate = &candidates[i];
|
||||
if (!best || candidate->vfb->colorBindSeq > best->vfb->colorBindSeq) {
|
||||
best = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
if (candidates.size() > 1) {
|
||||
std::string log;
|
||||
for (auto &candidate : candidates) {
|
||||
log += " - " + candidate.ToString() + "\n";
|
||||
if (Reporting::ShouldLogNTimes("mulblock", 5)) {
|
||||
std::string log;
|
||||
for (size_t i = 0; i < candidates.size(); i++) {
|
||||
log += " - " + candidates[i].ToString() + "\n";
|
||||
}
|
||||
WARN_LOG(G3D, "Multiple framebuffer candidates for %08x/%d/%d %d,%d %dx%d (dest = %d):\n%s", basePtr, stride_pixels, bpp, x_pixels, y, w_pixels, h, (int)destination, log.c_str());
|
||||
}
|
||||
WARN_LOG_N_TIMES(mulblock, 5, G3D, "Multiple framebuffer candidates for %08x/%d/%d %d,%d %dx%d (dest = %d):\n%s", basePtr, stride_pixels, bpp, x_pixels, y, w_pixels, h, (int)destination, log.c_str());
|
||||
}
|
||||
|
||||
if (!candidates.empty()) {
|
||||
// Pick the last candidate.
|
||||
*rect = candidates.back();
|
||||
*rect = *best;
|
||||
return true;
|
||||
} else {
|
||||
if (Memory::IsVRAMAddress(basePtr) && destination && h >= 128) {
|
||||
@@ -1798,7 +1823,7 @@ VirtualFramebuffer *FramebufferManagerCommon::CreateRAMFramebuffer(uint32_t fbAd
|
||||
}
|
||||
|
||||
// 1:1 pixel sides buffers, we resize buffers to these before we read them back.
|
||||
VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFramebuffer *vfb) {
|
||||
VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFramebuffer *vfb, RasterChannel channel) {
|
||||
// For now we'll keep these on the same struct as the ones that can get displayed
|
||||
// (and blatantly copy work already done above while at it).
|
||||
VirtualFramebuffer *nvfb = nullptr;
|
||||
@@ -1837,12 +1862,13 @@ VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFram
|
||||
|
||||
char name[64];
|
||||
snprintf(name, sizeof(name), "download_temp");
|
||||
nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, false, name });
|
||||
// TODO: We don't have a way to create a depth-only framebuffer yet.
|
||||
// Also, at least on Vulkan we always create both depth and color, need to rework how we handle renderpasses.
|
||||
nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, channel == RASTER_DEPTH ? true : false, name });
|
||||
if (!nvfb->fbo) {
|
||||
ERROR_LOG(FRAMEBUF, "Error creating FBO! %d x %d", nvfb->renderWidth, nvfb->renderHeight);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bvfbs_.push_back(nvfb);
|
||||
} else {
|
||||
UpdateDownloadTempBuffer(nvfb);
|
||||
@@ -2012,7 +2038,7 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst
|
||||
dstBasePtr, dstRect.x_bytes / bpp, dstRect.y, dstStride);
|
||||
FlushBeforeCopy();
|
||||
// Some backends can handle blitting within a framebuffer. Others will just have to deal with it or ignore it, apparently.
|
||||
BlitFramebuffer(dstRect.vfb, dstX, dstY, srcRect.vfb, srcX, srcY, dstRect.w_bytes / bpp, dstRect.h / bpp, bpp, "Blit_IntraBufferBlockTransfer");
|
||||
BlitFramebuffer(dstRect.vfb, dstX, dstY, srcRect.vfb, srcX, srcY, dstRect.w_bytes / bpp, dstRect.h / bpp, bpp, RASTER_COLOR, "Blit_IntraBufferBlockTransfer");
|
||||
RebindFramebuffer("rebind after intra block transfer");
|
||||
SetColorUpdated(dstRect.vfb, skipDrawReason);
|
||||
return true; // Skip the memory copy.
|
||||
@@ -2033,7 +2059,7 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst
|
||||
bpp = buffer_bpp;
|
||||
}
|
||||
FlushBeforeCopy();
|
||||
BlitFramebuffer(dstRect.vfb, dstRect.x_bytes / bpp, dstRect.y, srcRect.vfb, srcRect.x_bytes / bpp, srcRect.y, srcRect.w_bytes / bpp, height, bpp, "Blit_InterBufferBlockTransfer");
|
||||
BlitFramebuffer(dstRect.vfb, dstRect.x_bytes / bpp, dstRect.y, srcRect.vfb, srcRect.x_bytes / bpp, srcRect.y, srcRect.w_bytes / bpp, height, bpp, RASTER_COLOR, "Blit_InterBufferBlockTransfer");
|
||||
RebindFramebuffer("RebindFramebuffer - Inter-buffer block transfer");
|
||||
SetColorUpdated(dstRect.vfb, skipDrawReason);
|
||||
return true;
|
||||
@@ -2068,7 +2094,7 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst
|
||||
if (tooTall) {
|
||||
WARN_LOG_ONCE(btdheight, G3D, "Block transfer download %08x -> %08x dangerous, %d+%d is taller than %d", srcBasePtr, dstBasePtr, srcRect.y, srcRect.h, srcRect.vfb->bufferHeight);
|
||||
}
|
||||
ReadFramebufferToMemory(srcRect.vfb, static_cast<int>(srcX * srcXFactor), srcY, static_cast<int>(srcRect.w_bytes * srcXFactor), srcRect.h);
|
||||
ReadFramebufferToMemory(srcRect.vfb, static_cast<int>(srcX * srcXFactor), srcY, static_cast<int>(srcRect.w_bytes * srcXFactor), srcRect.h, RASTER_COLOR);
|
||||
srcRect.vfb->usageFlags = (srcRect.vfb->usageFlags | FB_USAGE_DOWNLOAD) & ~FB_USAGE_DOWNLOAD_CLEAR;
|
||||
}
|
||||
}
|
||||
@@ -2248,7 +2274,7 @@ void FramebufferManagerCommon::ShowScreenResolution() {
|
||||
|
||||
// We might also want to implement an asynchronous callback-style version of this. Would probably
|
||||
// only be possible to implement optimally on Vulkan, but on GL and D3D11 we could do pixel buffers
|
||||
// and read on the next frame, then call the callback. PackFramebufferAsync_ on OpenGL already does something similar.
|
||||
// and read on the next frame, then call the callback.
|
||||
//
|
||||
// The main use cases for this are:
|
||||
// * GE debugging(in practice async will not matter because it will stall anyway.)
|
||||
@@ -2286,7 +2312,7 @@ bool FramebufferManagerCommon::GetFramebuffer(u32 fb_address, int fb_stride, GEB
|
||||
tempVfb.renderWidth = w;
|
||||
tempVfb.renderHeight = h;
|
||||
tempVfb.renderScaleFactor = maxScaleFactor;
|
||||
BlitFramebuffer(&tempVfb, 0, 0, vfb, 0, 0, vfb->width, vfb->height, 0, "Blit_GetFramebuffer");
|
||||
BlitFramebuffer(&tempVfb, 0, 0, vfb, 0, 0, vfb->width, vfb->height, 0, RASTER_COLOR, "Blit_GetFramebuffer");
|
||||
|
||||
bound = tempFBO;
|
||||
} else {
|
||||
@@ -2395,33 +2421,33 @@ bool FramebufferManagerCommon::GetOutputFramebuffer(GPUDebugBuffer &buffer) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
// This function takes an already correctly-sized framebuffer and packs it into RAM.
|
||||
// This function takes an already correctly-sized framebuffer and reads it into emulated PSP VRAM.
|
||||
// Does not need to account for scaling.
|
||||
//
|
||||
// Color conversion is currently done on CPU but should theoretically be done on GPU.
|
||||
// (Except using the GPU might cause problems because of various implementations'
|
||||
// dithering behavior and games that expect exact colors like Danganronpa, so we
|
||||
// can't entirely be rid of the CPU path.) -- unknown
|
||||
void FramebufferManagerCommon::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) {
|
||||
if (!vfb->fbo) {
|
||||
ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferSync_: vfb->fbo == 0");
|
||||
return;
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::PackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel) {
|
||||
if (w <= 0 || h <= 0) {
|
||||
ERROR_LOG(G3D, "Bad inputs to PackFramebufferSync_: %d %d %d %d", x, y, w, h);
|
||||
ERROR_LOG(G3D, "Bad inputs to PackFramebufferSync: %d %d %d %d", x, y, w, h);
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 fb_address = vfb->fb_address & 0x3FFFFFFF;
|
||||
|
||||
Draw::DataFormat destFormat = GEFormatToThin3D(vfb->fb_format);
|
||||
Draw::DataFormat destFormat = channel == RASTER_COLOR ? GEFormatToThin3D(vfb->fb_format) : GEFormatToThin3D(GE_FORMAT_DEPTH16);
|
||||
const int dstBpp = (int)DataFormatSizeInBytes(destFormat);
|
||||
|
||||
const int dstByteOffset = (y * vfb->fb_stride + x) * dstBpp;
|
||||
const int dstSize = (h * vfb->fb_stride + w - 1) * dstBpp;
|
||||
int stride = channel == RASTER_COLOR ? vfb->fb_stride : vfb->z_stride;
|
||||
|
||||
const int dstByteOffset = (y * stride + x) * dstBpp;
|
||||
// Leave the gap between the end of the last line and the full stride.
|
||||
// This is only used for the NotifyMemInfo range.
|
||||
const int dstSize = (h * stride + w - 1) * dstBpp;
|
||||
|
||||
if (!Memory::IsValidRange(fb_address + dstByteOffset, dstSize)) {
|
||||
ERROR_LOG_REPORT(G3D, "PackFramebufferSync_ would write outside of memory, ignoring");
|
||||
ERROR_LOG_REPORT(G3D, "PackFramebufferSync would write outside of memory, ignoring");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2432,18 +2458,18 @@ void FramebufferManagerCommon::PackFramebufferSync_(VirtualFramebuffer *vfb, int
|
||||
DEBUG_LOG(G3D, "Reading framebuffer to mem, fb_address = %08x, ptr=%p", fb_address, destPtr);
|
||||
|
||||
if (destPtr) {
|
||||
draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, x, y, w, h, destFormat, destPtr, vfb->fb_stride, "PackFramebufferSync_");
|
||||
draw_->CopyFramebufferToMemorySync(vfb->fbo, channel == RASTER_COLOR ? Draw::FB_COLOR_BIT : Draw::FB_DEPTH_BIT, x, y, w, h, destFormat, destPtr, vfb->fb_stride, "PackFramebufferSync");
|
||||
char tag[128];
|
||||
size_t len = snprintf(tag, sizeof(tag), "FramebufferPack/%08x_%08x_%dx%d_%s", vfb->fb_address, vfb->z_address, w, h, GeBufferFormatToString(vfb->fb_format));
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, fb_address + dstByteOffset, dstSize, tag, len);
|
||||
} else {
|
||||
ERROR_LOG(G3D, "PackFramebufferSync_: Tried to readback to bad address %08x (stride = %d)", fb_address + dstByteOffset, vfb->fb_stride);
|
||||
ERROR_LOG(G3D, "PackFramebufferSync: Tried to readback to bad address %08x (stride = %d)", fb_address + dstByteOffset, vfb->fb_stride);
|
||||
}
|
||||
|
||||
gpuStats.numReadbacks++;
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h) {
|
||||
void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel) {
|
||||
// Clamp to bufferWidth. Sometimes block transfers can cause this to hit.
|
||||
if (x + w >= vfb->bufferWidth) {
|
||||
w = vfb->bufferWidth - x;
|
||||
@@ -2481,13 +2507,13 @@ void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb,
|
||||
}
|
||||
|
||||
if (vfb->renderWidth == vfb->width && vfb->renderHeight == vfb->height) {
|
||||
// No need to blit
|
||||
PackFramebufferSync_(vfb, x, y, w, h);
|
||||
// No need to stretch-blit
|
||||
PackFramebufferSync(vfb, x, y, w, h, channel);
|
||||
} else {
|
||||
VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb);
|
||||
VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb, channel);
|
||||
if (nvfb) {
|
||||
BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0, "Blit_ReadFramebufferToMemory");
|
||||
PackFramebufferSync_(nvfb, x, y, w, h);
|
||||
BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0, channel, "Blit_ReadFramebufferToMemory");
|
||||
PackFramebufferSync(nvfb, x, y, w, h, channel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2536,10 +2562,10 @@ void FramebufferManagerCommon::DownloadFramebufferForClut(u32 fb_address, u32 lo
|
||||
vfb->clutUpdatedBytes = loadBytes;
|
||||
|
||||
// We'll pseudo-blit framebuffers here to get a resized version of vfb.
|
||||
VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb);
|
||||
VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb, RASTER_COLOR);
|
||||
if (nvfb) {
|
||||
BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0, "Blit_DownloadFramebufferForClut");
|
||||
PackFramebufferSync_(nvfb, x, y, w, h);
|
||||
BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0, RASTER_COLOR, "Blit_DownloadFramebufferForClut");
|
||||
PackFramebufferSync(nvfb, x, y, w, h, RASTER_COLOR);
|
||||
}
|
||||
|
||||
textureCache_->ForgetLastTexture();
|
||||
@@ -2664,9 +2690,7 @@ void FramebufferManagerCommon::DrawActiveTexture(float x, float y, float w, floa
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE);
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, const char *tag) {
|
||||
RasterChannel channel = RASTER_COLOR;
|
||||
|
||||
void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, RasterChannel channel, const char *tag) {
|
||||
if (!dst->fbo || !src->fbo || !useBufferedRendering_) {
|
||||
// This can happen if they recently switched from non-buffered.
|
||||
if (useBufferedRendering_) {
|
||||
@@ -2697,8 +2721,8 @@ void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX
|
||||
return;
|
||||
}
|
||||
|
||||
bool useBlit = draw_->GetDeviceCaps().framebufferBlitSupported;
|
||||
bool useCopy = draw_->GetDeviceCaps().framebufferCopySupported;
|
||||
bool useBlit = channel == RASTER_COLOR ? draw_->GetDeviceCaps().framebufferBlitSupported : false;
|
||||
bool useCopy = channel == RASTER_COLOR ? draw_->GetDeviceCaps().framebufferCopySupported : false;
|
||||
if (dst == currentRenderVfb_) {
|
||||
// If already bound, using either a blit or a copy is unlikely to be an optimization.
|
||||
// So we're gonna use a raster draw instead.
|
||||
@@ -2859,3 +2883,37 @@ VirtualFramebuffer *FramebufferManagerCommon::ResolveFramebufferColorToFormat(Vi
|
||||
vfb->colorBindSeq = GetBindSeqCount();
|
||||
return vfb;
|
||||
}
|
||||
|
||||
static void ApplyKillzoneFramebufferSplit(FramebufferHeuristicParams *params, int *drawing_width) {
|
||||
// Detect whether we're rendering to the margin.
|
||||
bool margin;
|
||||
if ((params->scissorRight - params->scissorLeft) == 32) {
|
||||
// Title screen has this easy case. It also uses non-through verts, so lucky for us that we have this.
|
||||
margin = true;
|
||||
} else if (params->scissorRight == 480) {
|
||||
margin = false;
|
||||
} else {
|
||||
// Go deep, look at the vertices. Killzone-specific, of course.
|
||||
margin = false;
|
||||
if ((gstate.vertType & 0xFFFFFF) == 0x00800102) { // through, u16, s16
|
||||
u16 *vdata = (u16 *)Memory::GetPointerUnchecked(gstate_c.vertexAddr);
|
||||
int v0PosU = vdata[0];
|
||||
int v0PosX = vdata[2];
|
||||
if (v0PosX >= 480 && v0PosU < 480) {
|
||||
// Texturing from surface, writing to margin
|
||||
margin = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (margin) {
|
||||
gstate_c.SetCurRTOffset(-480, 0);
|
||||
// Modify the fb_address and z_address too to avoid matching below.
|
||||
params->fb_address += 480 * 4;
|
||||
params->z_address += 480 * 2;
|
||||
*drawing_width = 32;
|
||||
} else {
|
||||
gstate_c.SetCurRTOffset(0, 0);
|
||||
*drawing_width = 480;
|
||||
}
|
||||
}
|
||||
@@ -165,10 +165,12 @@ struct FramebufferHeuristicParams {
|
||||
bool isBlending;
|
||||
int viewportWidth;
|
||||
int viewportHeight;
|
||||
int regionWidth;
|
||||
int regionHeight;
|
||||
int scissorWidth;
|
||||
int scissorHeight;
|
||||
int16_t regionWidth;
|
||||
int16_t regionHeight;
|
||||
int16_t scissorLeft;
|
||||
int16_t scissorTop;
|
||||
int16_t scissorRight;
|
||||
int16_t scissorBottom;
|
||||
};
|
||||
|
||||
struct GPUgstate;
|
||||
@@ -206,7 +208,7 @@ enum class TempFBO {
|
||||
STENCIL,
|
||||
};
|
||||
|
||||
inline Draw::DataFormat GEFormatToThin3D(int geFormat) {
|
||||
inline Draw::DataFormat GEFormatToThin3D(GEBufferFormat geFormat) {
|
||||
switch (geFormat) {
|
||||
case GE_FORMAT_4444:
|
||||
return Draw::DataFormat::A4R4G4B4_UNORM_PACK16;
|
||||
@@ -216,7 +218,10 @@ inline Draw::DataFormat GEFormatToThin3D(int geFormat) {
|
||||
return Draw::DataFormat::R5G6B5_UNORM_PACK16;
|
||||
case GE_FORMAT_8888:
|
||||
return Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
case GE_FORMAT_DEPTH16:
|
||||
return Draw::DataFormat::D16;
|
||||
default:
|
||||
// TODO: Assert?
|
||||
return Draw::DataFormat::UNDEFINED;
|
||||
}
|
||||
}
|
||||
@@ -240,10 +245,6 @@ struct BlockTransferRect {
|
||||
int x_pixels() const {
|
||||
return x_bytes / BufferFormatBytesPerPixel(vfb->fb_format);
|
||||
}
|
||||
|
||||
bool operator < (const BlockTransferRect &other) const {
|
||||
return vfb->colorBindSeq < other.vfb->colorBindSeq;
|
||||
}
|
||||
};
|
||||
|
||||
namespace Draw {
|
||||
@@ -276,7 +277,7 @@ public:
|
||||
void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format);
|
||||
void DestroyFramebuf(VirtualFramebuffer *v);
|
||||
|
||||
VirtualFramebuffer *DoSetRenderFrameBuffer(const FramebufferHeuristicParams ¶ms, u32 skipDrawReason);
|
||||
VirtualFramebuffer *DoSetRenderFrameBuffer(FramebufferHeuristicParams ¶ms, u32 skipDrawReason);
|
||||
VirtualFramebuffer *SetRenderFrameBuffer(bool framebufChanged, int skipDrawReason) {
|
||||
// Inlining this part since it's so frequent.
|
||||
if (!framebufChanged && currentRenderVfb_) {
|
||||
@@ -305,7 +306,7 @@ public:
|
||||
|
||||
bool NotifyFramebufferCopy(u32 src, u32 dest, int size, bool isMemset, u32 skipDrawReason);
|
||||
void NotifyVideoUpload(u32 addr, int size, int width, GEBufferFormat fmt);
|
||||
void UpdateFromMemory(u32 addr, int size, bool safe);
|
||||
void UpdateFromMemory(u32 addr, int size);
|
||||
void ApplyClearToMemory(int x1, int y1, int x2, int y2, u32 clearColor);
|
||||
bool PerformStencilUpload(u32 addr, int size, StencilUpload flags);
|
||||
|
||||
@@ -320,7 +321,7 @@ public:
|
||||
void NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason);
|
||||
|
||||
bool BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags);
|
||||
void ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h);
|
||||
void ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel);
|
||||
|
||||
void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes);
|
||||
void DrawFramebufferToOutput(const u8 *srcPixels, int srcStride, GEBufferFormat srcPixelFormat);
|
||||
@@ -420,7 +421,7 @@ public:
|
||||
VirtualFramebuffer *ResolveFramebufferColorToFormat(VirtualFramebuffer *vfb, GEBufferFormat newFormat);
|
||||
|
||||
protected:
|
||||
virtual void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h);
|
||||
virtual void PackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel);
|
||||
void SetViewport2D(int x, int y, int w, int h);
|
||||
Draw::Texture *MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height);
|
||||
void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags);
|
||||
@@ -436,7 +437,7 @@ protected:
|
||||
virtual void DecimateFBOs(); // keeping it virtual to let D3D do a little extra
|
||||
|
||||
// Used by ReadFramebufferToMemory and later framebuffer block copies
|
||||
void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, const char *tag);
|
||||
void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, RasterChannel channel, const char *tag);
|
||||
|
||||
void BlitUsingRaster(
|
||||
Draw::Framebuffer *src, float srcX1, float srcY1, float srcX2, float srcY2,
|
||||
@@ -461,7 +462,7 @@ protected:
|
||||
|
||||
bool FindTransferFramebuffer(u32 basePtr, int stride, int x, int y, int w, int h, int bpp, bool destination, BlockTransferRect *rect);
|
||||
|
||||
VirtualFramebuffer *FindDownloadTempBuffer(VirtualFramebuffer *vfb);
|
||||
VirtualFramebuffer *FindDownloadTempBuffer(VirtualFramebuffer *vfb, RasterChannel channel);
|
||||
virtual void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) {}
|
||||
|
||||
VirtualFramebuffer *CreateRAMFramebuffer(uint32_t fbAddress, int width, int height, int stride, GEBufferFormat format);
|
||||
|
||||
@@ -581,8 +581,11 @@ void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, flo
|
||||
renderHeightFactor = renderHeight / 272.0f;
|
||||
}
|
||||
|
||||
renderX = gstate_c.curRTOffsetX;
|
||||
renderY = gstate_c.curRTOffsetY;
|
||||
// We take care negative offsets of in the projection matrix.
|
||||
// These come from split framebuffers (Killzone).
|
||||
// TODO: Might be safe to do get rid of this here and do the same for positive offsets?
|
||||
renderX = std::max(gstate_c.curRTOffsetX, 0);
|
||||
renderY = std::max(gstate_c.curRTOffsetY, 0);
|
||||
|
||||
// Scissor
|
||||
int scissorX1 = gstate.getScissorX1();
|
||||
@@ -609,6 +612,9 @@ void ConvertViewportAndScissor(bool useBufferedRendering, float renderWidth, flo
|
||||
float offsetY = gstate.getOffsetY();
|
||||
|
||||
if (out.throughMode) {
|
||||
// If renderX/renderY are offset to compensate for a split framebuffer,
|
||||
// applying the offset to the viewport isn't enough, since the viewport clips.
|
||||
// We need to apply either directly to the vertices, or to the "through" projection matrix.
|
||||
out.viewportX = renderX * renderWidthFactor + displayOffsetX;
|
||||
out.viewportY = renderY * renderHeightFactor + displayOffsetY;
|
||||
out.viewportW = curRTWidth * renderWidthFactor;
|
||||
|
||||
@@ -152,6 +152,13 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView
|
||||
if (!useBufferedRendering && g_display_rotation != DisplayRotation::ROTATE_0) {
|
||||
proj_through = proj_through * g_display_rot_matrix;
|
||||
}
|
||||
|
||||
// Negative RT offsets come from split framebuffers (Killzone)
|
||||
if (gstate_c.curRTOffsetX < 0 || gstate_c.curRTOffsetY < 0) {
|
||||
proj_through.wx += 2.0f * (float)gstate_c.curRTOffsetX / (float)gstate_c.curRTWidth;
|
||||
proj_through.wy += 2.0f * (float)gstate_c.curRTOffsetY / (float)gstate_c.curRTHeight;
|
||||
}
|
||||
|
||||
CopyMatrix4x4(ub->proj_through, proj_through.getReadPtr());
|
||||
ub->rotation = useBufferedRendering ? 0 : (float)g_display_rotation;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/Data/Convert/ColorConv.h"
|
||||
#include "Common/Data/Collections/TinySet.h"
|
||||
#include "Common/Profiler/Profiler.h"
|
||||
#include "Common/MemoryUtil.h"
|
||||
#include "Common/StringUtils.h"
|
||||
@@ -532,21 +533,17 @@ TexCacheEntry *TextureCacheCommon::SetTexture() {
|
||||
def.format = texFormat;
|
||||
def.bufw = bufw;
|
||||
|
||||
std::vector<AttachCandidate> candidates = GetFramebufferCandidates(def, 0);
|
||||
if (candidates.size() > 0) {
|
||||
int index = GetBestCandidateIndex(candidates);
|
||||
if (index != -1) {
|
||||
// If we had a texture entry here, let's get rid of it.
|
||||
if (entryIter != cache_.end()) {
|
||||
DeleteTexture(entryIter);
|
||||
}
|
||||
|
||||
const AttachCandidate &candidate = candidates[index];
|
||||
nextTexture_ = nullptr;
|
||||
nextNeedsRebuild_ = false;
|
||||
SetTextureFramebuffer(candidate); // sets curTexture3D
|
||||
return nullptr;
|
||||
AttachCandidate bestCandidate;
|
||||
if (GetBestFramebufferCandidate(def, 0, &bestCandidate)) {
|
||||
// If we had a texture entry here, let's get rid of it.
|
||||
if (entryIter != cache_.end()) {
|
||||
DeleteTexture(entryIter);
|
||||
}
|
||||
|
||||
nextTexture_ = nullptr;
|
||||
nextNeedsRebuild_ = false;
|
||||
SetTextureFramebuffer(bestCandidate); // sets curTexture3D
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Didn't match a framebuffer, keep going.
|
||||
@@ -617,55 +614,69 @@ TexCacheEntry *TextureCacheCommon::SetTexture() {
|
||||
return entry;
|
||||
}
|
||||
|
||||
std::vector<AttachCandidate> TextureCacheCommon::GetFramebufferCandidates(const TextureDefinition &entry, u32 texAddrOffset) {
|
||||
bool TextureCacheCommon::GetBestFramebufferCandidate(const TextureDefinition &entry, u32 texAddrOffset, AttachCandidate *bestCandidate) const {
|
||||
gpuStats.numFramebufferEvaluations++;
|
||||
|
||||
std::vector<AttachCandidate> candidates;
|
||||
TinySet<AttachCandidate, 6> candidates;
|
||||
|
||||
const std::vector<VirtualFramebuffer *> &framebuffers = framebufferManager_->Framebuffers();
|
||||
|
||||
for (VirtualFramebuffer *framebuffer : framebuffers) {
|
||||
FramebufferMatchInfo match{};
|
||||
if (MatchFramebuffer(entry, framebuffer, texAddrOffset, RASTER_COLOR, &match)) {
|
||||
candidates.push_back(AttachCandidate{ match, entry, framebuffer, RASTER_COLOR, framebuffer->colorBindSeq });
|
||||
candidates.push_back(AttachCandidate{ framebuffer, match, RASTER_COLOR });
|
||||
}
|
||||
match = {};
|
||||
if (MatchFramebuffer(entry, framebuffer, texAddrOffset, RASTER_DEPTH, &match)) {
|
||||
candidates.push_back(AttachCandidate{ match, entry, framebuffer, RASTER_DEPTH, framebuffer->depthBindSeq });
|
||||
candidates.push_back(AttachCandidate{ framebuffer, match, RASTER_DEPTH });
|
||||
}
|
||||
}
|
||||
|
||||
if (candidates.size() > 1) {
|
||||
if (candidates.size() == 0) {
|
||||
return false;
|
||||
} else if (candidates.size() == 1) {
|
||||
*bestCandidate = candidates[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Reporting::ShouldLogNTimes("multifbcandidate", 5)) {
|
||||
std::string cands;
|
||||
for (auto &candidate : candidates) {
|
||||
cands += candidate.ToString() + "\n";
|
||||
for (size_t i = 0; i < candidates.size(); i++) {
|
||||
cands += candidates[i].ToString() + "\n";
|
||||
}
|
||||
|
||||
WARN_LOG_REPORT_ONCE(multifbcandidate, G3D, "GetFramebufferCandidates: Multiple (%d) candidate framebuffers. texaddr: %08x offset: %d (%dx%d stride %d, %s):\n%s",
|
||||
WARN_LOG(G3D, "GetFramebufferCandidates: Multiple (%d) candidate framebuffers. texaddr: %08x offset: %d (%dx%d stride %d, %s):\n%s",
|
||||
(int)candidates.size(),
|
||||
entry.addr, texAddrOffset, dimWidth(entry.dim), dimHeight(entry.dim), entry.bufw, GeTextureFormatToString(entry.format),
|
||||
cands.c_str()
|
||||
);
|
||||
}
|
||||
|
||||
return candidates;
|
||||
}
|
||||
|
||||
int TextureCacheCommon::GetBestCandidateIndex(const std::vector<AttachCandidate> &candidates) {
|
||||
_dbg_assert_(!candidates.empty());
|
||||
|
||||
if (candidates.size() == 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// OK, multiple possible candidates. Will need to figure out which one is the most relevant.
|
||||
int bestRelevancy = -1;
|
||||
int bestIndex = -1;
|
||||
size_t bestIndex = -1;
|
||||
|
||||
bool kzCompat = PSP_CoreParameter().compat.flags().SplitFramebufferMargin;
|
||||
|
||||
// We simply use the sequence counter as relevancy nowadays.
|
||||
for (int i = 0; i < (int)candidates.size(); i++) {
|
||||
for (size_t i = 0; i < candidates.size(); i++) {
|
||||
const AttachCandidate &candidate = candidates[i];
|
||||
int relevancy = candidate.seqCount;
|
||||
int relevancy = candidate.channel == RASTER_COLOR ? candidate.fb->colorBindSeq : candidate.fb->depthBindSeq;
|
||||
|
||||
// Add a small negative penalty if the texture is currently bound as a framebuffer, and offset is not zero.
|
||||
// Should avoid problems when pingponging two nearby buffers, like in Wipeout Pure in #15927.
|
||||
if (candidate.channel == RASTER_COLOR &&
|
||||
(candidate.match.yOffset != 0 || candidate.match.xOffset != 0) &&
|
||||
(candidate.fb->fb_address & 0x1FFFFF) == (gstate.getFrameBufAddress() & 0x1FFFFF)) {
|
||||
relevancy -= 2;
|
||||
}
|
||||
|
||||
// Avoid binding as texture the framebuffer we're rendering to.
|
||||
// In Killzone, we split the framebuffer but the matching algorithm can still pick the wrong one,
|
||||
// which this avoids completely.
|
||||
if (kzCompat && candidate.fb == framebufferManager_->GetCurrentRenderVFB()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (relevancy > bestRelevancy) {
|
||||
bestRelevancy = relevancy;
|
||||
@@ -673,7 +684,12 @@ int TextureCacheCommon::GetBestCandidateIndex(const std::vector<AttachCandidate>
|
||||
}
|
||||
}
|
||||
|
||||
return bestIndex;
|
||||
if (bestIndex != -1) {
|
||||
*bestCandidate = candidates[bestIndex];
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Removes old textures.
|
||||
@@ -850,7 +866,7 @@ bool TextureCacheCommon::MatchFramebuffer(
|
||||
uint32_t fb_stride = channel == RASTER_DEPTH ? framebuffer->z_stride : framebuffer->fb_stride;
|
||||
GEBufferFormat fb_format = channel == RASTER_DEPTH ? GE_FORMAT_DEPTH16 : framebuffer->fb_format;
|
||||
|
||||
if (channel == RASTER_DEPTH && framebuffer->z_address == framebuffer->fb_address) {
|
||||
if (channel == RASTER_DEPTH && (framebuffer->z_address == framebuffer->fb_address || framebuffer->z_address == 0)) {
|
||||
// Try to avoid silly matches to somewhat malformed buffers.
|
||||
return false;
|
||||
}
|
||||
@@ -920,15 +936,23 @@ bool TextureCacheCommon::MatchFramebuffer(
|
||||
(fb_format == GE_FORMAT_8888 && entry.format == GE_TFMT_CLUT32) ||
|
||||
(fb_format != GE_FORMAT_8888 && entry.format == GE_TFMT_CLUT16);
|
||||
|
||||
const u32 bitOffset = (texaddr - addr) * 8;
|
||||
const int bitOffset = (texaddr - addr) * 8;
|
||||
if (bitOffset != 0) {
|
||||
const u32 pixelOffset = bitOffset / std::max(1U, (u32)textureBitsPerPixel[entry.format]);
|
||||
const int pixelOffset = bitOffset / (int)std::max(1U, (u32)textureBitsPerPixel[entry.format]);
|
||||
|
||||
matchInfo->yOffset = entry.bufw == 0 ? 0 : pixelOffset / entry.bufw;
|
||||
matchInfo->xOffset = entry.bufw == 0 ? 0 : pixelOffset % entry.bufw;
|
||||
if (pixelOffset > 0) {
|
||||
matchInfo->yOffset = entry.bufw == 0 ? 0 : pixelOffset / (int)entry.bufw;
|
||||
matchInfo->xOffset = entry.bufw == 0 ? 0 : pixelOffset % (int)entry.bufw;
|
||||
} else if (pixelOffset < 0) {
|
||||
// We don't support negative Y offsets, and negative X offsets are only for the Killzone workaround.
|
||||
if (pixelOffset < -(int)entry.bufw || !PSP_CoreParameter().compat.flags().SplitFramebufferMargin) {
|
||||
return false;
|
||||
}
|
||||
matchInfo->xOffset = entry.bufw == 0 ? 0 : -(-pixelOffset % (int)entry.bufw);
|
||||
}
|
||||
}
|
||||
|
||||
if (matchInfo->yOffset + minSubareaHeight >= framebuffer->height) {
|
||||
if (matchInfo->yOffset > 0 && matchInfo->yOffset + minSubareaHeight >= framebuffer->height) {
|
||||
// Can't be inside the framebuffer.
|
||||
return false;
|
||||
}
|
||||
@@ -1082,15 +1106,13 @@ bool TextureCacheCommon::SetOffsetTexture(u32 yOffset) {
|
||||
def.bufw = GetTextureBufw(0, texaddr, fmt);
|
||||
def.dim = gstate.getTextureDimension(0);
|
||||
|
||||
std::vector<AttachCandidate> candidates = GetFramebufferCandidates(def, texaddrOffset);
|
||||
if (candidates.size() > 0) {
|
||||
int index = GetBestCandidateIndex(candidates);
|
||||
if (index != -1) {
|
||||
SetTextureFramebuffer(candidates[index]);
|
||||
return true;
|
||||
}
|
||||
AttachCandidate bestCandidate;
|
||||
if (GetBestFramebufferCandidate(def, texaddrOffset, &bestCandidate)) {
|
||||
SetTextureFramebuffer(bestCandidate);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TextureCacheCommon::NotifyConfigChanged() {
|
||||
@@ -1164,7 +1186,7 @@ void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) {
|
||||
|
||||
// It's possible for a game to (successfully) access outside valid memory.
|
||||
u32 bytes = Memory::ValidSize(clutAddr, loadBytes);
|
||||
if (clutRenderAddress_ != 0xFFFFFFFF && !g_Config.bDisableSlowFramebufEffects) {
|
||||
if (clutRenderAddress_ != 0xFFFFFFFF && !g_Config.bBlockTransferGPU) {
|
||||
framebufferManager_->DownloadFramebufferForClut(clutRenderAddress_, clutRenderOffset_ + bytes);
|
||||
Memory::MemcpyUnchecked(clutBufRaw_, clutAddr, bytes);
|
||||
if (bytes < loadBytes) {
|
||||
@@ -1903,7 +1925,7 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
|
||||
bool smoothedDepal = false;
|
||||
u32 depthUpperBits = 0;
|
||||
|
||||
if (need_depalettize && !g_Config.bDisableSlowFramebufEffects) {
|
||||
if (need_depalettize) {
|
||||
clutTexture = textureShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBufRaw_);
|
||||
smoothedDepal = CanUseSmoothDepal(gstate, framebuffer->fb_format, clutTexture.rampLength);
|
||||
|
||||
@@ -2227,7 +2249,7 @@ void TextureCacheCommon::ClearNextFrame() {
|
||||
std::string AttachCandidate::ToString() const {
|
||||
return StringFromFormat("[%s seq:%d C:%08x/%d(%s) Z:%08x/%d X:%d Y:%d reint: %s]",
|
||||
this->channel == RASTER_COLOR ? "COLOR" : "DEPTH",
|
||||
this->seqCount,
|
||||
this->channel == RASTER_COLOR ? this->fb->colorBindSeq : this->fb->depthBindSeq,
|
||||
this->fb->fb_address, this->fb->fb_stride, GeBufferFormatToString(this->fb->fb_format),
|
||||
this->fb->z_address, this->fb->z_stride,
|
||||
this->match.xOffset, this->match.yOffset, this->match.reinterpret ? "true" : "false");
|
||||
@@ -2319,16 +2341,10 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt
|
||||
plan.w = gstate.getTextureWidth(0);
|
||||
plan.h = gstate.getTextureHeight(0);
|
||||
|
||||
plan.replaced = &FindReplacement(entry, plan.w, plan.h, plan.depth);
|
||||
if (plan.replaced->Valid()) {
|
||||
// We're replacing, so we won't scale.
|
||||
plan.scaleFactor = 1;
|
||||
plan.levelsToLoad = plan.replaced->NumLevels();
|
||||
plan.badMipSizes = false;
|
||||
}
|
||||
bool isPPGETexture = entry->addr > 0x05000000 && entry->addr < PSP_GetKernelMemoryEnd();
|
||||
|
||||
// Don't scale the PPGe texture.
|
||||
if (entry->addr > 0x05000000 && entry->addr < PSP_GetKernelMemoryEnd()) {
|
||||
if (isPPGETexture) {
|
||||
plan.scaleFactor = 1;
|
||||
}
|
||||
|
||||
@@ -2364,6 +2380,27 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt
|
||||
}
|
||||
}
|
||||
|
||||
if (plan.isVideo || isPPGETexture) {
|
||||
plan.replaced = &replacer_.FindNone();
|
||||
plan.replaceValid = false;
|
||||
} else {
|
||||
plan.replaced = &FindReplacement(entry, plan.w, plan.h, plan.depth);
|
||||
plan.replaceValid = plan.replaced->Valid();
|
||||
}
|
||||
|
||||
if (plan.replaceValid) {
|
||||
// We're replacing, so we won't scale.
|
||||
plan.scaleFactor = 1;
|
||||
plan.levelsToLoad = plan.replaced->NumLevels();
|
||||
plan.levelsToCreate = std::min(plan.levelsToLoad, plan.levelsToCreate);
|
||||
plan.badMipSizes = false;
|
||||
// But, we still need to create the texture at a larger size.
|
||||
plan.replaced->GetSize(0, plan.createW, plan.createH);
|
||||
} else {
|
||||
plan.createW = plan.w * plan.scaleFactor;
|
||||
plan.createH = plan.h * plan.scaleFactor;
|
||||
}
|
||||
|
||||
// Always load base level texture here
|
||||
plan.baseLevelSrc = 0;
|
||||
if (IsFakeMipmapChange()) {
|
||||
@@ -2376,7 +2413,7 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt
|
||||
if (plan.isVideo || plan.depth != 1) {
|
||||
plan.maxPossibleLevels = 1;
|
||||
} else {
|
||||
plan.maxPossibleLevels = log2i(std::min(plan.w * plan.scaleFactor, plan.h * plan.scaleFactor)) + 1;
|
||||
plan.maxPossibleLevels = log2i(std::min(plan.createW, plan.createH)) + 1;
|
||||
}
|
||||
|
||||
if (plan.levelsToCreate == 1) {
|
||||
|
||||
@@ -93,12 +93,11 @@ class VulkanTexture;
|
||||
// Enough information about a texture to match it to framebuffers.
|
||||
struct TextureDefinition {
|
||||
u32 addr;
|
||||
u16 bufw;
|
||||
u16 dim;
|
||||
GETextureFormat format;
|
||||
u32 dim;
|
||||
u32 bufw;
|
||||
};
|
||||
|
||||
|
||||
// TODO: Shrink this struct. There is some fluff.
|
||||
|
||||
// NOTE: These only handle textures loaded directly from PSP memory contents.
|
||||
@@ -209,18 +208,16 @@ typedef std::map<u64, std::unique_ptr<TexCacheEntry>> TexCache;
|
||||
#endif
|
||||
|
||||
struct FramebufferMatchInfo {
|
||||
u32 xOffset;
|
||||
u32 yOffset;
|
||||
int16_t xOffset;
|
||||
int16_t yOffset;
|
||||
bool reinterpret;
|
||||
GEBufferFormat reinterpretTo;
|
||||
};
|
||||
|
||||
struct AttachCandidate {
|
||||
FramebufferMatchInfo match;
|
||||
TextureDefinition entry;
|
||||
VirtualFramebuffer *fb;
|
||||
FramebufferMatchInfo match;
|
||||
RasterChannel channel;
|
||||
int seqCount;
|
||||
|
||||
std::string ToString() const;
|
||||
};
|
||||
@@ -264,11 +261,30 @@ struct BuildTexturePlan {
|
||||
int w;
|
||||
int h;
|
||||
|
||||
// Scaled (or replaced) size of the 0-mip of the final texture.
|
||||
int createW;
|
||||
int createH;
|
||||
|
||||
// Used for 3D textures only. If not a 3D texture, will be 1.
|
||||
int depth;
|
||||
|
||||
// The replacement for the texture.
|
||||
ReplacedTexture *replaced;
|
||||
// Need to only check once since it can change during the load!
|
||||
bool replaceValid;
|
||||
|
||||
void GetMipSize(int level, int *w, int *h) const {
|
||||
if (replaceValid) {
|
||||
replaced->GetSize(level, *w, *h);
|
||||
} else if (depth == 1) {
|
||||
*w = createW >> level;
|
||||
*h = createH >> level;
|
||||
} else {
|
||||
// 3D texture, we look for layers instead of levels.
|
||||
*w = createW;
|
||||
*h = createH;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class TextureCacheCommon {
|
||||
@@ -362,8 +378,7 @@ protected:
|
||||
|
||||
bool MatchFramebuffer(const TextureDefinition &entry, VirtualFramebuffer *framebuffer, u32 texaddrOffset, RasterChannel channel, FramebufferMatchInfo *matchInfo) const;
|
||||
|
||||
std::vector<AttachCandidate> GetFramebufferCandidates(const TextureDefinition &entry, u32 texAddrOffset);
|
||||
int GetBestCandidateIndex(const std::vector<AttachCandidate> &candidates);
|
||||
bool GetBestFramebufferCandidate(const TextureDefinition &entry, u32 texAddrOffset, AttachCandidate *bestCandidate) const;
|
||||
|
||||
void SetTextureFramebuffer(const AttachCandidate &candidate);
|
||||
|
||||
|
||||
@@ -176,9 +176,6 @@ void PrintDecodedVertex(VertexReader &vtx) {
|
||||
printf("P: %f %f %f\n", pos[0], pos[1], pos[2]);
|
||||
}
|
||||
|
||||
VertexDecoder::VertexDecoder() : decoded_(nullptr), ptr_(nullptr), jitted_(0), jittedSize_(0) {
|
||||
}
|
||||
|
||||
void VertexDecoder::Step_WeightsU8() const
|
||||
{
|
||||
u8 *wt = (u8 *)(decoded_ + decFmt.w0off);
|
||||
|
||||
@@ -125,10 +125,13 @@ public:
|
||||
case DEC_FLOAT_3:
|
||||
{
|
||||
const float *f = (const float *)(data_ + decFmt_.posoff);
|
||||
memcpy(pos, f, 12);
|
||||
if (isThrough()) {
|
||||
pos[0] = f[0];
|
||||
pos[1] = f[1];
|
||||
if (!isThrough()) {
|
||||
pos[2] = f[2];
|
||||
} else {
|
||||
// Integer value passed in a float. Clamped to 0, 65535.
|
||||
const float z = (int)pos[2] * (1.0f / 65535.0f);
|
||||
const float z = (int)f[2] * (1.0f / 65535.0f);
|
||||
pos[2] = z > 1.0f ? 1.0f : (z < 0.0f ? 0.0f : z);
|
||||
}
|
||||
}
|
||||
@@ -443,8 +446,6 @@ struct VertexDecoderOptions {
|
||||
|
||||
class VertexDecoder {
|
||||
public:
|
||||
VertexDecoder();
|
||||
|
||||
// A jit cache is not mandatory.
|
||||
void SetVertexType(u32 vtype, const VertexDecoderOptions &options, VertexDecoderJitCache *jitCache = nullptr);
|
||||
|
||||
@@ -549,11 +550,10 @@ public:
|
||||
int ToString(char *output) const;
|
||||
|
||||
// Mutable decoder state
|
||||
mutable u8 *decoded_;
|
||||
mutable const u8 *ptr_;
|
||||
|
||||
JittedVertexDecoder jitted_;
|
||||
int32_t jittedSize_;
|
||||
mutable u8 *decoded_ = nullptr;
|
||||
mutable const u8 *ptr_ = nullptr;
|
||||
JittedVertexDecoder jitted_ = 0;
|
||||
int32_t jittedSize_ = 0;
|
||||
|
||||
// "Immutable" state, set at startup
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ void DrawEngineD3D11::ApplyDrawState(int prim) {
|
||||
bool useBufferedRendering = framebufferManager_->UseBufferedRendering();
|
||||
// Blend
|
||||
if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) {
|
||||
gstate_c.SetAllowFramebufferRead(!g_Config.bDisableSlowFramebufEffects);
|
||||
gstate_c.SetAllowFramebufferRead(!g_Config.bDisableShaderBlending);
|
||||
if (gstate.isModeClear()) {
|
||||
keys_.blend.value = 0; // full wipe
|
||||
keys_.blend.blendEnable = false;
|
||||
|
||||
@@ -276,26 +276,27 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
int tw = plan.w;
|
||||
int th = plan.h;
|
||||
|
||||
DXGI_FORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat());
|
||||
if (plan.replaced->GetSize(plan.baseLevelSrc, tw, th)) {
|
||||
if (plan.replaceValid) {
|
||||
dstFmt = ToDXGIFormat(plan.replaced->Format(plan.baseLevelSrc));
|
||||
} else if (plan.scaleFactor > 1) {
|
||||
tw *= plan.scaleFactor;
|
||||
th *= plan.scaleFactor;
|
||||
dstFmt = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
}
|
||||
|
||||
// We don't yet have mip generation, so clamp the number of levels to the ones we can load directly.
|
||||
int levels;;
|
||||
int levels;
|
||||
|
||||
ID3D11ShaderResourceView *view;
|
||||
ID3D11Resource *texture = DxTex(entry);
|
||||
_assert_(texture == nullptr);
|
||||
|
||||
int tw;
|
||||
int th;
|
||||
plan.GetMipSize(0, &tw, &th);
|
||||
|
||||
if (plan.depth == 1) {
|
||||
// We don't yet have mip generation, so clamp the number of levels to the ones we can load directly.
|
||||
levels = std::min(plan.levelsToCreate, plan.levelsToLoad);
|
||||
|
||||
ID3D11Texture2D *tex;
|
||||
D3D11_TEXTURE2D_DESC desc{};
|
||||
desc.CPUAccessFlags = 0;
|
||||
@@ -305,13 +306,11 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) {
|
||||
desc.Width = tw;
|
||||
desc.Height = th;
|
||||
desc.Format = dstFmt;
|
||||
desc.MipLevels = plan.levelsToCreate;
|
||||
desc.MipLevels = levels;
|
||||
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
|
||||
ASSERT_SUCCESS(device_->CreateTexture2D(&desc, nullptr, &tex));
|
||||
texture = tex;
|
||||
|
||||
levels = std::min(plan.levelsToCreate, plan.levelsToLoad);
|
||||
} else {
|
||||
ID3D11Texture3D *tex;
|
||||
D3D11_TEXTURE3D_DESC desc{};
|
||||
@@ -338,45 +337,43 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) {
|
||||
for (int i = 0; i < levels; i++) {
|
||||
int srcLevel = (i == 0) ? plan.baseLevelSrc : i;
|
||||
|
||||
int w = gstate.getTextureWidth(srcLevel);
|
||||
int h = gstate.getTextureHeight(srcLevel);
|
||||
int mipWidth;
|
||||
int mipHeight;
|
||||
plan.GetMipSize(i, &mipWidth, &mipHeight);
|
||||
|
||||
u8 *data = nullptr;
|
||||
int stride = 0;
|
||||
int bpp = 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);
|
||||
if (plan.replaceValid) {
|
||||
bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel));
|
||||
} else {
|
||||
if (plan.scaleFactor > 1) {
|
||||
data = (u8 *)AllocateAlignedMemory(4 * (w * plan.scaleFactor) * (h * plan.scaleFactor), 16);
|
||||
stride = w * plan.scaleFactor * 4;
|
||||
bpp = 4;
|
||||
} else {
|
||||
int bpp = dstFmt == DXGI_FORMAT_B8G8R8A8_UNORM ? 4 : 2;
|
||||
|
||||
stride = std::max(w * bpp, 16);
|
||||
data = (u8 *)AllocateAlignedMemory(stride * h, 16);
|
||||
bpp = dstFmt == DXGI_FORMAT_B8G8R8A8_UNORM ? 4 : 2;
|
||||
}
|
||||
}
|
||||
|
||||
stride = std::max(mipWidth * bpp, 16);
|
||||
data = (u8 *)AllocateAlignedMemory(stride * mipHeight, 16);
|
||||
|
||||
if (!data) {
|
||||
ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", w, h);
|
||||
ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", mipWidth, mipHeight);
|
||||
return;
|
||||
}
|
||||
|
||||
LoadTextureLevel(*entry, data, stride, *plan.replaced, srcLevel, plan.scaleFactor, texFmt, false);
|
||||
|
||||
if (plan.depth == 1) {
|
||||
context_->UpdateSubresource(texture, i, nullptr, data, stride, 0);
|
||||
} else {
|
||||
D3D11_BOX box{};
|
||||
box.front = i;
|
||||
box.back = i + 1;
|
||||
box.right = w * plan.scaleFactor;
|
||||
box.bottom = h * plan.scaleFactor;
|
||||
box.right = mipWidth;
|
||||
box.bottom = mipHeight;
|
||||
context_->UpdateSubresource(texture, 0, &box, data, stride, 0);
|
||||
}
|
||||
FreeAlignedMemory(data);
|
||||
@@ -393,7 +390,7 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) {
|
||||
entry->status &= ~TexCacheEntry::STATUS_NO_MIPS;
|
||||
}
|
||||
|
||||
if (plan.replaced->Valid()) {
|
||||
if (plan.replaceValid) {
|
||||
entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,9 +133,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferManagerDX9::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) {
|
||||
if (!vfb->fbo) {
|
||||
ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferDirectx9_: vfb->fbo == 0");
|
||||
void FramebufferManagerDX9::PackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel) {
|
||||
if (channel != RASTER_COLOR) {
|
||||
// Unsupported
|
||||
WARN_LOG_ONCE(d3ddepthreadback, G3D, "Not yet supporting depth readbacks on DX9");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -175,11 +176,6 @@
|
||||
}
|
||||
|
||||
void FramebufferManagerDX9::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h) {
|
||||
if (!vfb->fbo) {
|
||||
ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackDepthbuffer: vfb->fbo == 0");
|
||||
return;
|
||||
}
|
||||
|
||||
// We always read the depth buffer in 24_8 format.
|
||||
const u32 z_address = vfb->z_address;
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ protected:
|
||||
void DecimateFBOs() override;
|
||||
|
||||
private:
|
||||
void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) override;
|
||||
void PackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel) override;
|
||||
void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h);
|
||||
bool GetRenderTargetFramebuffer(LPDIRECT3DSURFACE9 renderTarget, LPDIRECT3DSURFACE9 offscreen, int w, int h, GPUDebugBuffer &buffer);
|
||||
|
||||
|
||||
@@ -119,8 +119,7 @@ void DrawEngineDX9::ApplyDrawState(int prim) {
|
||||
bool useBufferedRendering = framebufferManager_->UseBufferedRendering();
|
||||
|
||||
if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) {
|
||||
// Unfortunately, this isn't implemented on DX9 yet.
|
||||
gstate_c.SetAllowFramebufferRead(false);
|
||||
gstate_c.SetAllowFramebufferRead(!g_Config.bDisableShaderBlending);
|
||||
if (gstate.isModeClear()) {
|
||||
dxstate.blend.disable();
|
||||
// Color Mask
|
||||
|
||||
@@ -238,27 +238,28 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
int tw = plan.w;
|
||||
int th = plan.h;
|
||||
|
||||
D3DFORMAT dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat());
|
||||
if (plan.replaced->GetSize(plan.baseLevelSrc, tw, th)) {
|
||||
if (plan.replaceValid) {
|
||||
dstFmt = ToD3D9Format(plan.replaced->Format(plan.baseLevelSrc));
|
||||
} else if (plan.scaleFactor > 1) {
|
||||
tw *= plan.scaleFactor;
|
||||
th *= plan.scaleFactor;
|
||||
dstFmt = D3DFMT_A8R8G8B8;
|
||||
}
|
||||
|
||||
// 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);
|
||||
int levels;
|
||||
|
||||
LPDIRECT3DBASETEXTURE9 &texture = DxTex(entry);
|
||||
D3DPOOL pool = D3DPOOL_DEFAULT;
|
||||
int usage = D3DUSAGE_DYNAMIC;
|
||||
|
||||
int tw;
|
||||
int th;
|
||||
plan.GetMipSize(0, &tw, &th);
|
||||
|
||||
HRESULT hr;
|
||||
if (plan.depth == 1) {
|
||||
// We don't yet have mip generation, so clamp the number of levels to the ones we can load directly.
|
||||
levels = std::min(plan.levelsToCreate, plan.levelsToLoad);
|
||||
|
||||
LPDIRECT3DTEXTURE9 tex;
|
||||
hr = device_->CreateTexture(tw, th, levels, usage, dstFmt, pool, &tex, nullptr);
|
||||
texture = tex;
|
||||
@@ -266,6 +267,8 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) {
|
||||
LPDIRECT3DVOLUMETEXTURE9 tex;
|
||||
hr = device_->CreateVolumeTexture(tw, th, plan.depth, 1, usage, dstFmt, pool, &tex, nullptr);
|
||||
texture = tex;
|
||||
|
||||
levels = 1;
|
||||
}
|
||||
|
||||
if (FAILED(hr)) {
|
||||
@@ -322,7 +325,7 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry) {
|
||||
entry->status |= TexCacheEntry::STATUS_3D;
|
||||
}
|
||||
|
||||
if (plan.replaced->Valid()) {
|
||||
if (plan.replaceValid) {
|
||||
entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
|
||||
bool useBufferedRendering = framebufferManager_->UseBufferedRendering();
|
||||
|
||||
if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) {
|
||||
gstate_c.SetAllowFramebufferRead(!g_Config.bDisableSlowFramebufEffects);
|
||||
gstate_c.SetAllowFramebufferRead(!g_Config.bDisableShaderBlending);
|
||||
|
||||
if (gstate.isModeClear()) {
|
||||
// Color Test
|
||||
|
||||
@@ -301,37 +301,36 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) {
|
||||
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);
|
||||
int mipWidth;
|
||||
int mipHeight;
|
||||
plan.GetMipSize(i, &mipWidth, &mipHeight);
|
||||
|
||||
u8 *data = nullptr;
|
||||
int stride = 0;
|
||||
int bpp;
|
||||
|
||||
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);
|
||||
if (plan.replaceValid) {
|
||||
bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel));
|
||||
} else {
|
||||
if (plan.scaleFactor > 1) {
|
||||
data = (u8 *)AllocateAlignedMemory(4 * (w * plan.scaleFactor) * (h * plan.scaleFactor), 16);
|
||||
stride = w * plan.scaleFactor * 4;
|
||||
bpp = 4;
|
||||
} else {
|
||||
int bpp = dstFmt == Draw::DataFormat::R8G8B8A8_UNORM ? 4 : 2;
|
||||
|
||||
stride = std::max(w * bpp, 4);
|
||||
data = (u8 *)AllocateAlignedMemory(stride * h, 16);
|
||||
bpp = dstFmt == Draw::DataFormat::R8G8B8A8_UNORM ? 4 : 2;
|
||||
}
|
||||
}
|
||||
|
||||
stride = mipWidth * bpp;
|
||||
data = (u8 *)AllocateAlignedMemory(stride * mipHeight, 16);
|
||||
|
||||
if (!data) {
|
||||
ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", w, h);
|
||||
ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", mipWidth, mipHeight);
|
||||
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, 1, dstFmt, data, GLRAllocType::ALIGNED);
|
||||
render_->TextureImage(entry->textureName, i, mipWidth, mipHeight, 1, dstFmt, data, GLRAllocType::ALIGNED);
|
||||
}
|
||||
|
||||
bool genMips = plan.levelsToCreate > plan.levelsToLoad;
|
||||
@@ -359,7 +358,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) {
|
||||
render_->FinalizeTexture(entry->textureName, 1, false);
|
||||
}
|
||||
|
||||
if (plan.replaced->Valid()) {
|
||||
if (plan.replaceValid) {
|
||||
entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus()));
|
||||
}
|
||||
}
|
||||
|
||||
+17
-6
@@ -1686,6 +1686,16 @@ void GPUCommon::Execute_Prim(u32 op, u32 diff) {
|
||||
}
|
||||
}
|
||||
|
||||
if (PSP_CoreParameter().compat.flags().SplitFramebufferMargin) {
|
||||
switch (gstate.vertType & 0xFFFFFF) {
|
||||
case 0x00800102: // through, u16 uv, u16 pos (used for the framebuffer effect in-game)
|
||||
case 0x0080011c: // through, 8888 color, s16 pos (used for clearing in the margin of the title screen)
|
||||
case 0x00000183: // float uv, float pos (used for drawing in the margin of the title screen)
|
||||
// Need to re-check the framebuffer every one of these draws, to update the split if needed.
|
||||
gstate_c.Dirty(DIRTY_FRAMEBUF);
|
||||
}
|
||||
}
|
||||
|
||||
// This also makes skipping drawing very effective.
|
||||
VirtualFramebuffer *vfb = framebufferManager_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason);
|
||||
if (blueToAlpha) {
|
||||
@@ -2854,9 +2864,10 @@ void GPUCommon::DoBlockTransfer(u32 skipDrawReason) {
|
||||
if (MemBlockInfoDetailed(numBytes, numBytes)) {
|
||||
const uint32_t src = srcBasePtr + (srcY * srcStride + srcX) * bpp;
|
||||
const uint32_t dst = dstBasePtr + (dstY * dstStride + dstX) * bpp;
|
||||
const std::string tag = "GPUBlockTransfer/" + GetMemWriteTagAt(src, srcSize);
|
||||
NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag.c_str(), tag.size());
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, dst, dstSize, tag.c_str(), tag.size());
|
||||
char tag[128];
|
||||
size_t tagSize = FormatMemWriteTagAt(tag, sizeof(tag), "GPUBlockTransfer/", src, srcSize);
|
||||
NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag, tagSize);
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, dst, dstSize, tag, tagSize);
|
||||
}
|
||||
|
||||
// TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate.
|
||||
@@ -2871,7 +2882,7 @@ bool GPUCommon::PerformMemoryCopy(u32 dest, u32 src, int size) {
|
||||
// Since they're identical we don't need to copy.
|
||||
if (!Memory::IsVRAMAddress(dest) || (dest ^ 0x00400000) != src) {
|
||||
if (MemBlockInfoDetailed(size)) {
|
||||
const std::string tag = "GPUMemcpy/" + GetMemWriteTagAt(src, size);
|
||||
const std::string tag = GetMemWriteTagAt("GPUMemcpy/", src, size);
|
||||
Memory::Memcpy(dest, src, size, tag.c_str(), tag.size());
|
||||
} else {
|
||||
Memory::Memcpy(dest, src, size, "GPUMemcpy");
|
||||
@@ -2883,7 +2894,7 @@ bool GPUCommon::PerformMemoryCopy(u32 dest, u32 src, int size) {
|
||||
}
|
||||
|
||||
if (MemBlockInfoDetailed(size)) {
|
||||
const std::string tag = "GPUMemcpy/" + GetMemWriteTagAt(src, size);
|
||||
const std::string tag = GetMemWriteTagAt("GPUMemcpy/", src, size);
|
||||
NotifyMemInfo(MemBlockFlags::READ, src, size, tag.c_str(), tag.size());
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, dest, size, tag.c_str(), tag.size());
|
||||
}
|
||||
@@ -2938,7 +2949,7 @@ void GPUCommon::InvalidateCache(u32 addr, int size, GPUInvalidationType type) {
|
||||
// Vempire invalidates (with writeback) after drawing, but before blitting.
|
||||
// TODO: Investigate whether we can get this to work some other way.
|
||||
if (type == GPU_INVALIDATE_SAFE) {
|
||||
framebufferManager_->UpdateFromMemory(addr, size, type == GPU_INVALIDATE_SAFE);
|
||||
framebufferManager_->UpdateFromMemory(addr, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -592,8 +592,8 @@ struct GPUStateCache {
|
||||
u32 curTextureHeight;
|
||||
u32 actualTextureHeight;
|
||||
// Only applied when needShaderTexClamp = true.
|
||||
u32 curTextureXOffset;
|
||||
u32 curTextureYOffset;
|
||||
int curTextureXOffset;
|
||||
int curTextureYOffset;
|
||||
bool curTextureIs3D;
|
||||
|
||||
float vpWidth;
|
||||
@@ -621,15 +621,15 @@ struct GPUStateCache {
|
||||
u32 curRTRenderWidth;
|
||||
u32 curRTRenderHeight;
|
||||
|
||||
void SetCurRTOffset(u32 xoff, u32 yoff) {
|
||||
void SetCurRTOffset(int xoff, int yoff) {
|
||||
if (xoff != curRTOffsetX || yoff != curRTOffsetY) {
|
||||
curRTOffsetX = xoff;
|
||||
curRTOffsetY = yoff;
|
||||
Dirty(DIRTY_VIEWPORTSCISSOR_STATE);
|
||||
Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_PROJTHROUGHMATRIX);
|
||||
}
|
||||
}
|
||||
u32 curRTOffsetX;
|
||||
u32 curRTOffsetY;
|
||||
int curRTOffsetX;
|
||||
int curRTOffsetY;
|
||||
|
||||
// Set if we are doing hardware bezier/spline.
|
||||
SubmitType submitType;
|
||||
|
||||
+297
-130
@@ -16,11 +16,12 @@
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "Core/MemMap.h"
|
||||
#include "GPU/ge_constants.h"
|
||||
#include "GPU/GPU.h"
|
||||
#include "GPU/GPUInterface.h"
|
||||
#include "GPU/GPUState.h"
|
||||
#include "GPU/GeDisasm.h"
|
||||
|
||||
void GeDescribeVertexType(u32 op, char *buffer, int len) {
|
||||
bool through = (op & GE_VTYPE_THROUGH_MASK) == GE_VTYPE_THROUGH;
|
||||
@@ -113,7 +114,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
break;
|
||||
|
||||
case GE_CMD_BASE:
|
||||
snprintf(buffer, bufsize, "BASE: %06x", data);
|
||||
if ((data & 0x000F0000) == data)
|
||||
snprintf(buffer, bufsize, "BASE: high=%02x", data >> 16);
|
||||
else
|
||||
snprintf(buffer, bufsize, "BASE: high=%02x (extra %06x)", data >> 16, data & ~0x000F0000);
|
||||
break;
|
||||
|
||||
case GE_CMD_VADDR:
|
||||
@@ -171,16 +175,16 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
|
||||
case GE_CMD_JUMP:
|
||||
{
|
||||
u32 target = (((gstate.base & 0x00FF0000) << 8) | (op & 0xFFFFFC)) & 0x0FFFFFFF;
|
||||
snprintf(buffer, bufsize, "JUMP: %08x to %08x", pc, target);
|
||||
u32 target = gstate_c.getRelativeAddress(op & 0x00FFFFFC);
|
||||
snprintf(buffer, bufsize, "JUMP: %08x to %08x (%06x)", pc, target, data);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_CMD_CALL:
|
||||
{
|
||||
u32 retval = pc + 4;
|
||||
u32 target = gstate_c.getRelativeAddress(op & 0xFFFFFF);
|
||||
snprintf(buffer, bufsize, "CALL: %08x to %08x, ret=%08x", pc, target, retval);
|
||||
u32 target = gstate_c.getRelativeAddress(op & 0x00FFFFFC);
|
||||
snprintf(buffer, bufsize, "CALL: %08x to %08x (%06x), ret=%08x", pc, target, data, retval);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -210,28 +214,45 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
int signal = prev & 0xFFFF;
|
||||
int enddata = data & 0xFFFFFF;
|
||||
// We should probably defer to sceGe here, no sense in implementing this stuff in every GPU
|
||||
u32 target = (((signal << 16) | (enddata & 0xFFFF)) & 0xFFFFFFFC);
|
||||
switch (behaviour) {
|
||||
case 1:
|
||||
case PSP_GE_SIGNAL_HANDLER_SUSPEND:
|
||||
snprintf(buffer, bufsize, "Signal with wait. signal/end: %04x %04x", signal, enddata);
|
||||
break;
|
||||
case 2:
|
||||
case PSP_GE_SIGNAL_HANDLER_CONTINUE:
|
||||
snprintf(buffer, bufsize, "Signal without wait. signal/end: %04x %04x", signal, enddata);
|
||||
break;
|
||||
case 3:
|
||||
case PSP_GE_SIGNAL_HANDLER_PAUSE:
|
||||
snprintf(buffer, bufsize, "Signal with pause. signal/end: %04x %04x", signal, enddata);
|
||||
break;
|
||||
case 8:
|
||||
case PSP_GE_SIGNAL_SYNC:
|
||||
snprintf(buffer, bufsize, "Signal with sync. signal/end: %04x %04x", signal, enddata);
|
||||
break;
|
||||
case 0x10:
|
||||
snprintf(buffer, bufsize, "Signal with jump. signal/end: %04x %04x", signal, enddata);
|
||||
case PSP_GE_SIGNAL_JUMP:
|
||||
snprintf(buffer, bufsize, "Signal with jump. signal/end: %04x %04x, target: %08x", signal, enddata, target);
|
||||
break;
|
||||
case 0x11:
|
||||
snprintf(buffer, bufsize, "Signal with call. signal/end: %04x %04x", signal, enddata);
|
||||
case PSP_GE_SIGNAL_CALL:
|
||||
snprintf(buffer, bufsize, "Signal with call. signal/end: %04x %04x, target: %08x", signal, enddata, target);
|
||||
break;
|
||||
case 0x12:
|
||||
case PSP_GE_SIGNAL_RET:
|
||||
snprintf(buffer, bufsize, "Signal with return. signal/end: %04x %04x", signal, enddata);
|
||||
break;
|
||||
case PSP_GE_SIGNAL_RJUMP:
|
||||
target += pc;
|
||||
snprintf(buffer, bufsize, "Signal with jump (relative.) signal/end: %04x %04x, target: %08x", signal, enddata, target);
|
||||
break;
|
||||
case PSP_GE_SIGNAL_RCALL:
|
||||
target += pc;
|
||||
snprintf(buffer, bufsize, "Signal with call (relative.) signal/end: %04x %04x, target: %08x", signal, enddata, target);
|
||||
break;
|
||||
case PSP_GE_SIGNAL_OJUMP:
|
||||
target = gstate_c.getRelativeAddress(target);
|
||||
snprintf(buffer, bufsize, "Signal with jump (offset.) signal/end: %04x %04x, target: %08x", signal, enddata, target);
|
||||
break;
|
||||
case PSP_GE_SIGNAL_OCALL:
|
||||
target = gstate_c.getRelativeAddress(target);
|
||||
snprintf(buffer, bufsize, "Signal with call (offset.) signal/end: %04x %04x, target: %08x", signal, enddata, target);
|
||||
break;
|
||||
default:
|
||||
snprintf(buffer, bufsize, "UNKNOWN Signal UNIMPLEMENTED %i! signal/end: %04x %04x", behaviour, signal, enddata);
|
||||
break;
|
||||
@@ -262,7 +283,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
break;
|
||||
|
||||
case GE_CMD_ORIGIN:
|
||||
snprintf(buffer, bufsize, "ORIGIN: %06x", data);
|
||||
if (data)
|
||||
snprintf(buffer, bufsize, "ORIGIN offset=%08x (extra %06x)", pc, data);
|
||||
else
|
||||
snprintf(buffer, bufsize, "ORIGIN offset=%08x", pc);
|
||||
break;
|
||||
|
||||
case GE_CMD_VERTEXTYPE:
|
||||
@@ -273,24 +297,24 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
break;
|
||||
|
||||
case GE_CMD_OFFSETADDR:
|
||||
snprintf(buffer, bufsize, "OffsetAddr: %06x", data);
|
||||
snprintf(buffer, bufsize, "OffsetAddr: %06x (offset=%08x)", data, data << 8);
|
||||
break;
|
||||
|
||||
case GE_CMD_REGION1:
|
||||
{
|
||||
int x1 = data & 0x3ff;
|
||||
int y1 = data >> 10;
|
||||
int y1 = (data >> 10) & 0x3ff;
|
||||
if (data & 0xF00000)
|
||||
snprintf(buffer, bufsize, "Region TL: %d %d (extra %x)", x1, y1, data >> 20);
|
||||
snprintf(buffer, bufsize, "Region Rate: %d %d (extra %x)", x1, y1, data >> 20);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Region TL: %d %d", x1, y1);
|
||||
snprintf(buffer, bufsize, "Region Rate: %d %d", x1, y1);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_CMD_REGION2:
|
||||
{
|
||||
int x2 = data & 0x3ff;
|
||||
int y2 = data >> 10;
|
||||
int y2 = (data >> 10) & 0x3ff;
|
||||
if (data & 0xF00000)
|
||||
snprintf(buffer, bufsize, "Region BR: %d %d (extra %x)", x2, y2, data >> 20);
|
||||
else
|
||||
@@ -349,7 +373,7 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
case GE_CMD_SCISSOR1:
|
||||
{
|
||||
int x1 = data & 0x3ff;
|
||||
int y1 = data >> 10;
|
||||
int y1 = (data >> 10) & 0x3ff;
|
||||
if (data & 0xF00000)
|
||||
snprintf(buffer, bufsize, "Scissor TL: %i, %i (extra %x)", x1, y1, data >> 20);
|
||||
else
|
||||
@@ -359,7 +383,7 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
case GE_CMD_SCISSOR2:
|
||||
{
|
||||
int x2 = data & 0x3ff;
|
||||
int y2 = data >> 10;
|
||||
int y2 = (data >> 10) & 0x3ff;
|
||||
if (data & 0xF00000)
|
||||
snprintf(buffer, bufsize, "Scissor BR: %i, %i (extra %x)", x2, y2, data >> 20);
|
||||
else
|
||||
@@ -368,33 +392,37 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
break;
|
||||
|
||||
case GE_CMD_MINZ:
|
||||
{
|
||||
float zMin = getFloat24(data) / 65535.f;
|
||||
snprintf(buffer, bufsize, "MinZ: %f", zMin);
|
||||
}
|
||||
if (data & 0xFF0000)
|
||||
snprintf(buffer, bufsize, "MinZ: 0x%04x / %f (extra %02x)", data & 0xFFFF, (float)(data & 0xFFFF) / 65535.0f, data >> 16);
|
||||
else
|
||||
snprintf(buffer, bufsize, "MinZ: 0x%04x / %f", data, (float)data / 65535.0f);
|
||||
break;
|
||||
|
||||
case GE_CMD_MAXZ:
|
||||
{
|
||||
float zMax = getFloat24(data) / 65535.f;
|
||||
snprintf(buffer, bufsize, "MaxZ: %f", zMax);
|
||||
}
|
||||
if (data & 0xFF0000)
|
||||
snprintf(buffer, bufsize, "MaxZ: 0x%04x / %f (extra %02x)", data & 0xFFFF, (float)(data & 0xFFFF) / 65535.0f, data >> 16);
|
||||
else
|
||||
snprintf(buffer, bufsize, "MaxZ: 0x%04x / %f", data, (float)data / 65535.0f);
|
||||
break;
|
||||
|
||||
case GE_CMD_FRAMEBUFPTR:
|
||||
{
|
||||
snprintf(buffer, bufsize, "FramebufPtr: %08x", data);
|
||||
snprintf(buffer, bufsize, "Framebuf ptr: 0x04%06x", data);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_CMD_FRAMEBUFWIDTH:
|
||||
{
|
||||
snprintf(buffer, bufsize, "FramebufWidth: %x, address high %02x", data & 0xFFFF, data >> 16);
|
||||
}
|
||||
if (data & ~0x07FC)
|
||||
snprintf(buffer, bufsize, "Framebuf stride: 0x%x (extra %06x)", data & 0x07FC, data & ~0x07FC);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Framebuf stride: %04x", data);
|
||||
break;
|
||||
|
||||
case GE_CMD_FRAMEBUFPIXFORMAT:
|
||||
snprintf(buffer, bufsize, "FramebufPixelFormat: %i", data);
|
||||
if (data <= 3)
|
||||
snprintf(buffer, bufsize, "Framebuf PixelFormat: %s", GeBufferFormatToString((GEBufferFormat)data));
|
||||
else
|
||||
snprintf(buffer, bufsize, "Framebuf PixelFormat: invalid %x", data);
|
||||
break;
|
||||
|
||||
case GE_CMD_TEXADDR0:
|
||||
@@ -405,7 +433,7 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
case GE_CMD_TEXADDR5:
|
||||
case GE_CMD_TEXADDR6:
|
||||
case GE_CMD_TEXADDR7:
|
||||
snprintf(buffer, bufsize, "Texture address %i: %06x", cmd-GE_CMD_TEXADDR0, data);
|
||||
snprintf(buffer, bufsize, "Texture address %d: low=%06x", cmd - GE_CMD_TEXADDR0, data);
|
||||
break;
|
||||
|
||||
case GE_CMD_TEXBUFWIDTH0:
|
||||
@@ -416,31 +444,53 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
case GE_CMD_TEXBUFWIDTH5:
|
||||
case GE_CMD_TEXBUFWIDTH6:
|
||||
case GE_CMD_TEXBUFWIDTH7:
|
||||
snprintf(buffer, bufsize, "Texture BUFWIDTH %i: %06x", cmd-GE_CMD_TEXBUFWIDTH0, data);
|
||||
snprintf(buffer, bufsize, "Texture stride %d: 0x%04x, address high=%02x", cmd - GE_CMD_TEXBUFWIDTH0, data & 0xFFFF, data >> 16);
|
||||
break;
|
||||
|
||||
case GE_CMD_CLUTADDR:
|
||||
snprintf(buffer, bufsize, "CLUT base addr: %06x", data);
|
||||
snprintf(buffer, bufsize, "CLUT addr: low=%06x", data);
|
||||
break;
|
||||
|
||||
case GE_CMD_CLUTADDRUPPER:
|
||||
snprintf(buffer, bufsize, "CLUT addr upper %08x", data);
|
||||
if ((data & 0x000F0000) == data)
|
||||
snprintf(buffer, bufsize, "CLUT addr: high=%02x", data >> 16);
|
||||
else
|
||||
snprintf(buffer, bufsize, "CLUT addr: high=%02x (extra %06x)", data >> 16, data & ~0x000F0000);
|
||||
break;
|
||||
|
||||
case GE_CMD_LOADCLUT:
|
||||
// This could be used to "dirty" textures with clut.
|
||||
if (data)
|
||||
snprintf(buffer, bufsize, "Clut load: %08x, %d bytes, %06x", gstate.getClutAddress(), (data & 0x3F) << 5, data & 0xFFFFC0);
|
||||
if ((data & 0xFFFFC0) != 0)
|
||||
snprintf(buffer, bufsize, "Clut load: %08x, %d bytes (extra %06x)", gstate.getClutAddress(), (data & 0x3F) << 5, data & 0xFFFFC0);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Clut load");
|
||||
snprintf(buffer, bufsize, "Clut load: %08x, %d bytes", gstate.getClutAddress(), (data & 0x3F) << 5);
|
||||
break;
|
||||
|
||||
case GE_CMD_TEXMAPMODE:
|
||||
snprintf(buffer, bufsize, "Tex map mode: %06x", data);
|
||||
{
|
||||
static const char *uvgen[] = {
|
||||
"texcoords",
|
||||
"texgen matrix",
|
||||
"env map",
|
||||
"invalid"
|
||||
};
|
||||
static const char *uvproj[] = {
|
||||
"pos",
|
||||
"uv",
|
||||
"normalized normal",
|
||||
"normal",
|
||||
};
|
||||
if ((data & 0x000303) == data)
|
||||
snprintf(buffer, bufsize, "Tex map mode: uvgen=%s, uvproj=%s", uvgen[data & 3], uvproj[(data >> 8) & 3]);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Tex map mode: uvgen=%s, uvproj=%s (extra %06x)", uvgen[data & 3], uvproj[(data >> 8) & 3], data & ~0x000303);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_CMD_TEXSHADELS:
|
||||
snprintf(buffer, bufsize, "Tex shade light sources: %06x", data);
|
||||
if ((data & 0x000303) == data)
|
||||
snprintf(buffer, bufsize, "Tex shade light sources: %d, %d", data & 3, (data >> 8) & 3);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Tex shade light sources: %d, %d (extra %06x)", data & 3, (data >> 8) & 3, data & ~0x000303);
|
||||
break;
|
||||
|
||||
case GE_CMD_CLUTFORMAT:
|
||||
@@ -458,9 +508,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
case GE_CMD_TRANSFERSRC:
|
||||
{
|
||||
if (data & 0xF)
|
||||
snprintf(buffer, bufsize, "Block transfer src: %06x (extra: %x)", data & ~0xF, data & 0xF);
|
||||
snprintf(buffer, bufsize, "Block transfer src: low=%06x (extra: %x)", data & ~0xF, data & 0xF);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Block transfer src: %06x", data);
|
||||
snprintf(buffer, bufsize, "Block transfer src: low=%06x", data);
|
||||
// Nothing to do, the next one prints
|
||||
}
|
||||
break;
|
||||
@@ -470,9 +520,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
u32 xferSrc = (gstate.transfersrc & 0x00FFFFFF) | ((data & 0xFF0000) << 8);
|
||||
u32 xferSrcW = data & 0x3FF;
|
||||
if (data & ~0xFF03FF)
|
||||
snprintf(buffer, bufsize, "Block transfer src: %08x W: %i (extra %x)", xferSrc, xferSrcW, data);
|
||||
snprintf(buffer, bufsize, "Block transfer src: high=%02x, w=%d (addr %08x, extra %x)", data >> 16, xferSrcW, xferSrc, data & ~0xFF03FF);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Block transfer src: %08x W: %i", xferSrc, xferSrcW);
|
||||
snprintf(buffer, bufsize, "Block transfer src: high=%02x, w=%d (addr %08x)", data >> 16, xferSrcW, xferSrc);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -480,9 +530,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
{
|
||||
// Nothing to do, the next one prints
|
||||
if (data & 0xF)
|
||||
snprintf(buffer, bufsize, "Block transfer dst: %06x (extra: %x)", data & ~0xF, data & 0xF);
|
||||
snprintf(buffer, bufsize, "Block transfer dst: low=%06x (extra: %x)", data & ~0xF, data & 0xF);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Block transfer dst: %06x", data);
|
||||
snprintf(buffer, bufsize, "Block transfer dst: low=%06x", data);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -491,9 +541,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
u32 xferDst = (gstate.transferdst & 0x00FFFFFF) | ((data & 0xFF0000) << 8);
|
||||
u32 xferDstW = data & 0x3FF;
|
||||
if (data & ~0xFF03FF)
|
||||
snprintf(buffer, bufsize, "Block transfer dest: %08x W: %i (extra %x)", xferDst, xferDstW, data);
|
||||
snprintf(buffer, bufsize, "Block transfer dst: high=%02x, w=%d (addr %08x, extra %x)", data >> 16, xferDstW, xferDst, data & ~0xFF03FF);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Block transfer dest: %08x W: %i", xferDst, xferDstW);
|
||||
snprintf(buffer, bufsize, "Block transfer dst: high=%02x, w=%d (addr %08x)", data >> 16, xferDstW, xferDst);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -513,9 +563,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
u32 x = (data & 1023);
|
||||
u32 y = ((data>>10) & 1023);
|
||||
if (data & 0xF00000)
|
||||
snprintf(buffer, bufsize, "Block transfer dest rect TL: %i, %i (extra %x)", x, y, data >> 20);
|
||||
snprintf(buffer, bufsize, "Block transfer dst rect TL: %d, %d (extra %x)", x, y, data >> 20);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Block transfer dest rect TL: %i, %i", x, y);
|
||||
snprintf(buffer, bufsize, "Block transfer dst rect TL: %d, %d", x, y);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -532,9 +582,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
|
||||
case GE_CMD_TRANSFERSTART:
|
||||
if (data & ~1)
|
||||
snprintf(buffer, bufsize, "Block transfer start: %d (extra %x)", data & 1, data & ~1);
|
||||
snprintf(buffer, bufsize, "Block transfer start: bpp=%d (extra %x)", (data & 1) ? 4 : 2, data & ~1);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Block transfer start: %d", data);
|
||||
snprintf(buffer, bufsize, "Block transfer start: bpp=%d", (data & 1) ? 4 : 2);
|
||||
break;
|
||||
|
||||
case GE_CMD_TEXSIZE0:
|
||||
@@ -548,7 +598,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
{
|
||||
int w = 1 << (data & 0xf);
|
||||
int h = 1 << ((data>>8) & 0xf);
|
||||
snprintf(buffer, bufsize, "Texture size %i: %06x, width : %d, height : %d", cmd - GE_CMD_TEXSIZE0, data, w, h);
|
||||
if ((data & ~0x0F0F) && w <= 512 && h <= 512)
|
||||
snprintf(buffer, bufsize, "Texture size %d: %dx%d", cmd - GE_CMD_TEXSIZE0, w, h);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Texture size %d: %dx%d (extra %06x)", cmd - GE_CMD_TEXSIZE0, w, h, data);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -559,7 +612,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
break;
|
||||
|
||||
case GE_CMD_ZBUFWIDTH:
|
||||
snprintf(buffer, bufsize, "Zbuf width: %06x", data);
|
||||
if (data & ~0x07FC)
|
||||
snprintf(buffer, bufsize, "Zbuf stride: 0x%x (extra %06x)", data & 0x07FC, data & ~0x07FC);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Zbuf stride: %04x", data);
|
||||
break;
|
||||
|
||||
case GE_CMD_AMBIENTCOLOR:
|
||||
@@ -567,7 +623,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
break;
|
||||
|
||||
case GE_CMD_AMBIENTALPHA:
|
||||
snprintf(buffer, bufsize, "Ambient alpha: %06x", data);
|
||||
if (data & ~0xFF)
|
||||
snprintf(buffer, bufsize, "Ambient alpha: %02x (extra %04x)", data & 0xFF, data >> 8);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Ambient alpha: %02x", data);
|
||||
break;
|
||||
|
||||
case GE_CMD_MATERIALAMBIENT:
|
||||
@@ -587,7 +646,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
break;
|
||||
|
||||
case GE_CMD_MATERIALALPHA:
|
||||
snprintf(buffer, bufsize, "Material alpha color: %06x", data);
|
||||
if (data & ~0xFF)
|
||||
snprintf(buffer, bufsize, "Material alpha: %02x (extra %04x)", data & 0xFF, data >> 8);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Material alpha: %02x", data);
|
||||
break;
|
||||
|
||||
case GE_CMD_MATERIALSPECULARCOEF:
|
||||
@@ -596,23 +658,43 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
|
||||
case GE_CMD_SHADEMODE:
|
||||
if (data & ~1)
|
||||
snprintf(buffer, bufsize, "Shade: %06x (%s, extra %x)", data, data ? "gouraud" : "flat", data);
|
||||
snprintf(buffer, bufsize, "Shade: %d (%s, extra %x)", data & 1, (data & 1) ? "gouraud" : "flat", data & ~1);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Shade: %06x (%s)", data, data ? "gouraud" : "flat");
|
||||
snprintf(buffer, bufsize, "Shade: %d (%s)", data & 1, (data & 1) ? "gouraud" : "flat");
|
||||
break;
|
||||
|
||||
case GE_CMD_LIGHTMODE:
|
||||
if (data & ~1)
|
||||
snprintf(buffer, bufsize, "Lightmode: %06x (%s, extra %x)", data, data ? "separate spec" : "single color", data);
|
||||
snprintf(buffer, bufsize, "Lightmode: %d (%s, extra %x)", data & 1, (data & 1) ? "separate spec" : "single color", data & ~1);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Lightmode: %06x (%s)", data, data ? "separate spec" : "single color");
|
||||
snprintf(buffer, bufsize, "Lightmode: %d (%s)", data & 1, (data & 1) ? "separate spec" : "single color");
|
||||
break;
|
||||
|
||||
case GE_CMD_LIGHTTYPE0:
|
||||
case GE_CMD_LIGHTTYPE1:
|
||||
case GE_CMD_LIGHTTYPE2:
|
||||
case GE_CMD_LIGHTTYPE3:
|
||||
snprintf(buffer, bufsize, "Light %i type: %06x", cmd-GE_CMD_LIGHTTYPE0, data);
|
||||
{
|
||||
static const char *lightComputations[] = {
|
||||
"diffuse",
|
||||
"diffuse + spec",
|
||||
"pow(diffuse)",
|
||||
"unknown (diffuse?)",
|
||||
};
|
||||
static const char *lightTypes[] = {
|
||||
"directional",
|
||||
"point",
|
||||
"spot",
|
||||
"unknown (directional?)",
|
||||
};
|
||||
const int comp = (data & 0x0003) >> 0;
|
||||
const int type = (data & 0x0300) >> 8;
|
||||
|
||||
if (data & ~0x0303)
|
||||
snprintf(buffer, bufsize, "Light %d type: %s, comp: %s (extra %06x)", cmd - GE_CMD_LIGHTTYPE0, lightTypes[type], lightComputations[comp], data & ~0x0303);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Light %d type: %s, comp: %s", cmd - GE_CMD_LIGHTTYPE0, lightTypes[type], lightComputations[comp]);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_CMD_LX0:case GE_CMD_LY0:case GE_CMD_LZ0:
|
||||
@@ -669,22 +751,22 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
break;
|
||||
|
||||
case GE_CMD_VIEWPORTXSCALE:
|
||||
snprintf(buffer, bufsize, "Viewport X scale: %f", getFloat24(data));
|
||||
break;
|
||||
case GE_CMD_VIEWPORTYSCALE:
|
||||
case GE_CMD_VIEWPORTXCENTER:
|
||||
case GE_CMD_VIEWPORTYCENTER:
|
||||
snprintf(buffer, bufsize, "Viewport param %i: %f", cmd-GE_CMD_VIEWPORTXSCALE, getFloat24(data));
|
||||
snprintf(buffer, bufsize, "Viewport Y scale: %f", getFloat24(data));
|
||||
break;
|
||||
case GE_CMD_VIEWPORTZSCALE:
|
||||
{
|
||||
float zScale = getFloat24(data) / 65535.f;
|
||||
snprintf(buffer, bufsize, "Viewport Z scale: %f", zScale);
|
||||
}
|
||||
snprintf(buffer, bufsize, "Viewport Z scale: %f", getFloat24(data));
|
||||
break;
|
||||
case GE_CMD_VIEWPORTXCENTER:
|
||||
snprintf(buffer, bufsize, "Viewport X center: %f", getFloat24(data));
|
||||
break;
|
||||
case GE_CMD_VIEWPORTYCENTER:
|
||||
snprintf(buffer, bufsize, "Viewport Y center: %f", getFloat24(data));
|
||||
break;
|
||||
case GE_CMD_VIEWPORTZCENTER:
|
||||
{
|
||||
float zOff = getFloat24(data) / 65535.f;
|
||||
snprintf(buffer, bufsize, "Viewport Z pos: %f", zOff);
|
||||
}
|
||||
snprintf(buffer, bufsize, "Viewport Z center: %f", getFloat24(data));
|
||||
break;
|
||||
|
||||
case GE_CMD_LIGHTENABLE0:
|
||||
@@ -695,34 +777,69 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
break;
|
||||
|
||||
case GE_CMD_CULL:
|
||||
snprintf(buffer, bufsize, "Cull: %06x", data);
|
||||
if (data & ~1)
|
||||
snprintf(buffer, bufsize, "Cull: %s (extra %06x)", (data & 1) ? "back (CCW)" : "front (CW)", data & ~1);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Cull: %s", (data & 1) ? "back (CCW)" : "front (CW)");
|
||||
break;
|
||||
|
||||
case GE_CMD_PATCHDIVISION:
|
||||
{
|
||||
int patch_div_s = data & 0xFF;
|
||||
int patch_div_t = (data >> 8) & 0xFF;
|
||||
if (data & 0xFF0000)
|
||||
snprintf(buffer, bufsize, "Patch subdivision: %i x %i (extra %x)", patch_div_s, patch_div_t, data & 0xFF0000);
|
||||
int patch_div_s = data & 0x7F;
|
||||
int patch_div_t = (data >> 8) & 0x7F;
|
||||
if (data & 0xFF8080)
|
||||
snprintf(buffer, bufsize, "Patch subdivision: %d x %d (extra %x)", patch_div_s, patch_div_t, data & 0xFF8080);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Patch subdivision: %i x %i", patch_div_s, patch_div_t);
|
||||
snprintf(buffer, bufsize, "Patch subdivision: %d x %d", patch_div_s, patch_div_t);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_CMD_PATCHPRIMITIVE:
|
||||
snprintf(buffer, bufsize, "Patch Primitive: %d", data);
|
||||
{
|
||||
static const char *patchPrims[] = {
|
||||
"triangles",
|
||||
"lines",
|
||||
"points",
|
||||
"unknown/points",
|
||||
};
|
||||
if (data & ~3)
|
||||
snprintf(buffer, bufsize, "Patch Primitive: %s (extra %06x)", patchPrims[data & 3], data & ~3);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Patch Primitive: %s", patchPrims[data & 3]);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_CMD_PATCHFACING:
|
||||
snprintf(buffer, bufsize, "Patch Facing: %d", data);
|
||||
if (data & ~1)
|
||||
snprintf(buffer, bufsize, "Patch Facing: %s (extra %06x)", (data & 1) ? "reversed normals" : "standard normals", data & ~1);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Patch Facing: %s", (data & 1) ? "reversed normals" : "standard normals");
|
||||
break;
|
||||
|
||||
case GE_CMD_REVERSENORMAL:
|
||||
snprintf(buffer, bufsize, "Reverse normal: %d", data);
|
||||
if (data & ~1)
|
||||
snprintf(buffer, bufsize, "Reverse normal: %s (extra %06x)", (data & 1) ? "reversed" : "standard", data & ~1);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Reverse normal: %s", (data & 1) ? "reversed" : "standard");
|
||||
break;
|
||||
|
||||
case GE_CMD_MATERIALUPDATE:
|
||||
snprintf(buffer, bufsize, "Material update: %d", data);
|
||||
{
|
||||
static const char *materialTypes[] = {
|
||||
"none",
|
||||
"ambient",
|
||||
"diffuse",
|
||||
"ambient, diffuse",
|
||||
"specular",
|
||||
"ambient, specular",
|
||||
"diffuse, specular",
|
||||
"ambient, diffuse, specular",
|
||||
};
|
||||
if (data & ~7)
|
||||
snprintf(buffer, bufsize, "Material update: %s (extra %06x)", materialTypes[data & 7], data & ~7);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Material update: %s", materialTypes[data & 7]);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -747,7 +864,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
mode = clearModes[(data >> 8) & 7];
|
||||
else
|
||||
mode = "off";
|
||||
snprintf(buffer, bufsize, "Clear mode: %06x (%s)", data, mode);
|
||||
if (data & ~0x0701)
|
||||
snprintf(buffer, bufsize, "Clear mode: %s (extra %06x)", mode, data & ~0x0701);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Clear mode: %s", mode);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -832,7 +952,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
case GE_CMD_ALPHATEST:
|
||||
{
|
||||
const char *alphaTestFuncs[] = { " NEVER ", " ALWAYS ", " == ", " != ", " < ", " <= ", " > ", " >= " };
|
||||
snprintf(buffer, bufsize, "Alpha test settings: %06x ((c & %02x)%s%02x)", data, (data >> 16) & 0xFF, alphaTestFuncs[data & 7], (data >> 8) & 0xFF);
|
||||
if (data & ~0xFFFF07)
|
||||
snprintf(buffer, bufsize, "Alpha test: (src.a & %02x)%s%02x (extra %06x)", (data >> 16) & 0xFF, alphaTestFuncs[data & 7], (data >> 8) & 0xFF, data & ~0xFFFF07);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Alpha test: (src.a & %02x)%s%02x", (data >> 16) & 0xFF, alphaTestFuncs[data & 7], (data >> 8) & 0xFF);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -860,25 +983,35 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
"blend",
|
||||
"replace",
|
||||
"add",
|
||||
"unsupported1",
|
||||
"unsupported2",
|
||||
"unsupported3",
|
||||
"add2",
|
||||
"add3",
|
||||
"add4",
|
||||
};
|
||||
if (data & ~0x10107)
|
||||
snprintf(buffer, bufsize, "TexFunc %i %s %s%s (extra %x)", data & 7, data & 0x100 ? "RGBA" : "RGB", texfuncs[data & 7], data & 0x10000 ? " color double" : "", data);
|
||||
snprintf(buffer, bufsize, "TexFunc %d %s %s%s (extra %x)", data & 7, data & 0x100 ? "RGBA" : "RGB", texfuncs[data & 7], data & 0x10000 ? ", color double" : "", data & ~0x10107);
|
||||
else
|
||||
snprintf(buffer, bufsize, "TexFunc %i %s %s%s", data & 7, data & 0x100 ? "RGBA" : "RGB", texfuncs[data & 7], data & 0x10000 ? " color double" : "");
|
||||
snprintf(buffer, bufsize, "TexFunc %d %s %s%s", data & 7, data & 0x100 ? "RGBA" : "RGB", texfuncs[data & 7], data & 0x10000 ? ", color double" : "");
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_CMD_TEXFILTER:
|
||||
{
|
||||
static const char *textureFilters[] = {
|
||||
"nearest",
|
||||
"linear",
|
||||
"nearest, invalid",
|
||||
"linear, invalid",
|
||||
"nearest, mipmap nearest",
|
||||
"linear, mipmap nearest",
|
||||
"nearest, mipmap linear",
|
||||
"linear, mipmap linear",
|
||||
};
|
||||
int min = data & 7;
|
||||
int mag = (data >> 8) & 1;
|
||||
if (data & ~0x107)
|
||||
snprintf(buffer, bufsize, "TexFilter min: %i mag: %i (extra %x)", min, mag, data);
|
||||
snprintf(buffer, bufsize, "TexFilter min: %s, mag: %s (extra %x)", textureFilters[min], textureFilters[mag], data & ~0x107);
|
||||
else
|
||||
snprintf(buffer, bufsize, "TexFilter min: %i mag: %i", min, mag);
|
||||
snprintf(buffer, bufsize, "TexFilter min: %s, mag: %s", textureFilters[min], textureFilters[mag]);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -887,12 +1020,15 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
break;
|
||||
|
||||
case GE_CMD_TEXMODE:
|
||||
snprintf(buffer, bufsize, "TexMode %06x (%s, %d levels, %s)", data, data & 1 ? "swizzle" : "no swizzle", (data >> 16) & 7, (data >> 8) & 1 ? "separate cluts" : "shared clut");
|
||||
if (data & ~0x070101)
|
||||
snprintf(buffer, bufsize, "TexMode %s, %d levels, %s (extra %06x)", (data & 1) ? "swizzle" : "no swizzle", (data >> 16) & 7, (data >> 8) & 1 ? "separate cluts" : "shared clut", data & ~0x070101);
|
||||
else
|
||||
snprintf(buffer, bufsize, "TexMode %s, %d levels, %s", (data & 1) ? "swizzle" : "no swizzle", (data >> 16) & 7, (data >> 8) & 1 ? "separate cluts" : "shared clut");
|
||||
break;
|
||||
|
||||
case GE_CMD_TEXFORMAT:
|
||||
{
|
||||
const char *texformats[] = {
|
||||
static const char *texformats[] = {
|
||||
"5650",
|
||||
"5551",
|
||||
"4444",
|
||||
@@ -910,7 +1046,10 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
"unsupported4",
|
||||
"unsupported5",
|
||||
};
|
||||
snprintf(buffer, bufsize, "TexFormat %06x (%s)", data, texformats[data & 0xF]);
|
||||
if (data & ~0xF)
|
||||
snprintf(buffer, bufsize, "TexFormat %s (extra %06x)", texformats[data & 0xF], data & ~0xF);
|
||||
else
|
||||
snprintf(buffer, bufsize, "TexFormat %s", texformats[data & 0xF]);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -930,24 +1069,34 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
|
||||
case GE_CMD_TEXWRAP:
|
||||
if (data & ~0x0101)
|
||||
snprintf(buffer, bufsize, "TexWrap %s s, %s t (extra %x)", data & 1 ? "clamp" : "wrap", data & 0x100 ? "clamp" : "wrap", data);
|
||||
snprintf(buffer, bufsize, "TexWrap %s s, %s t (extra %x)", data & 1 ? "clamp" : "wrap", data & 0x100 ? "clamp" : "wrap", data & ~0x0101);
|
||||
else
|
||||
snprintf(buffer, bufsize, "TexWrap %s s, %s t", data & 1 ? "clamp" : "wrap", data & 0x100 ? "clamp" : "wrap");
|
||||
break;
|
||||
|
||||
case GE_CMD_TEXLEVEL:
|
||||
if (data & ~0xFF0003)
|
||||
snprintf(buffer, bufsize, "TexLevel mode: %i Offset: %i (extra %x)", data&3, data >> 16, data);
|
||||
else
|
||||
snprintf(buffer, bufsize, "TexLevel mode: %i Offset: %i", data&3, data >> 16);
|
||||
{
|
||||
static const char *mipLevelModes[] = {
|
||||
"auto + bias",
|
||||
"bias",
|
||||
"slope + bias",
|
||||
"invalid + bias",
|
||||
};
|
||||
const int biasFixed = (s8)(data >> 16);
|
||||
const float bias = (float)biasFixed / 16.0f;
|
||||
if (data & ~0xFF0003)
|
||||
snprintf(buffer, bufsize, "TexLevel mode: %s Offset: %f / %d (extra %x)", mipLevelModes[data & 3], bias, biasFixed, data & ~0xFF0003);
|
||||
else
|
||||
snprintf(buffer, bufsize, "TexLevel mode: %s Offset: %f / %d", mipLevelModes[data & 3], bias, biasFixed);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_CMD_FOG1:
|
||||
snprintf(buffer, bufsize, "Fog1 %f", getFloat24(data));
|
||||
snprintf(buffer, bufsize, "Fog end %f", getFloat24(data));
|
||||
break;
|
||||
|
||||
case GE_CMD_FOG2:
|
||||
snprintf(buffer, bufsize, "Fog2 %f", getFloat24(data));
|
||||
snprintf(buffer, bufsize, "Fog slope %f", getFloat24(data));
|
||||
break;
|
||||
|
||||
case GE_CMD_FOGCOLOR:
|
||||
@@ -955,7 +1104,7 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
break;
|
||||
|
||||
case GE_CMD_TEXLODSLOPE:
|
||||
snprintf(buffer, bufsize, "TexLodSlope %06x", data);
|
||||
snprintf(buffer, bufsize, "TexLodSlope %f", getFloat24(data));
|
||||
break;
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
@@ -971,15 +1120,18 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
|
||||
case GE_CMD_STENCILOP:
|
||||
{
|
||||
const char *stencilOps[] = { "KEEP", "ZERO", "REPLACE", "INVERT", "INCREMENT", "DECREMENT", "unsupported1", "unsupported2" };
|
||||
static const char *stencilOps[] = { "KEEP", "ZERO", "REPLACE", "INVERT", "INCREMENT", "DECREMENT", "unsupported1", "unsupported2" };
|
||||
snprintf(buffer, bufsize, "Stencil op: fail=%s, pass/depthfail=%s, pass=%s", stencilOps[data & 7], stencilOps[(data >> 8) & 7], stencilOps[(data >> 16) & 7]);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_CMD_STENCILTEST:
|
||||
{
|
||||
const char *zTestFuncs[] = { "NEVER", "ALWAYS", " == ", " != ", " < ", " <= ", " > ", " >= " };
|
||||
snprintf(buffer, bufsize, "Stencil test: %06x (%02x %s (c & %02x))", data, (data >> 8) & 0xFF, zTestFuncs[data & 7], (data >> 16) & 0xFF);
|
||||
static const char *zTestFuncs[] = { " NEVER ", " ALWAYS ", " == ", " != ", " < ", " <= ", " > ", " >= " };
|
||||
if (data & ~0xFFFF07)
|
||||
snprintf(buffer, bufsize, "Stencil test: %02x%s(dst.a & %02x) (extra %06x)", (data >> 8) & 0xFF, zTestFuncs[data & 7], (data >> 16) & 0xFF, data & ~0xFFFF07);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Stencil test: %02x%s(dst.a & %02x)", (data >> 8) & 0xFF, zTestFuncs[data & 7], (data >> 16) & 0xFF);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -989,8 +1141,11 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
|
||||
case GE_CMD_ZTEST:
|
||||
{
|
||||
const char *zTestFuncs[] = { "NEVER", "ALWAYS", " == ", " != ", " < ", " <= ", " > ", " >= " };
|
||||
snprintf(buffer, bufsize, "Z test mode: %i (%s)", data, zTestFuncs[data & 7]);
|
||||
static const char *zTestFuncs[] = { "NEVER", "ALWAYS", "==", "!=", "<", "<=", ">", ">=" };
|
||||
if (data & ~7)
|
||||
snprintf(buffer, bufsize, "Z test mode: %s (extra %06x)", zTestFuncs[data & 7], data & ~7);
|
||||
else
|
||||
snprintf(buffer, bufsize, "Z test mode: %s", zTestFuncs[data & 7]);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1036,18 +1191,27 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
"negated and",
|
||||
"set",
|
||||
};
|
||||
snprintf(buffer, bufsize, "LogicOp: %06x (%s)", data, logicOps[data & 0xF]);
|
||||
if (data & ~0xF)
|
||||
snprintf(buffer, bufsize, "LogicOp: %s (%06x)", logicOps[data & 0xF], data & ~0xF);
|
||||
else
|
||||
snprintf(buffer, bufsize, "LogicOp: %s", logicOps[data & 0xF]);
|
||||
}
|
||||
break;
|
||||
|
||||
case GE_CMD_ZWRITEDISABLE:
|
||||
snprintf(buffer, bufsize, "ZMask: %06x", data);
|
||||
if (data & ~1)
|
||||
snprintf(buffer, bufsize, "ZMask: %s (extra %06x)", data & 1 ? "disable write" : "allow write", data & ~1);
|
||||
else
|
||||
snprintf(buffer, bufsize, "ZMask: %s", data & 1 ? "disable write" : "allow write");
|
||||
break;
|
||||
|
||||
case GE_CMD_COLORTEST:
|
||||
{
|
||||
const char *colorTests[] = {"NEVER", "ALWAYS", " == ", " != "};
|
||||
snprintf(buffer, bufsize, "ColorTest: %06x (ref%s(c & cmask))", data, colorTests[data & 3]);
|
||||
const char *colorTests[] = {" NEVER ", " ALWAYS ", " == ", " != "};
|
||||
if (data & ~3)
|
||||
snprintf(buffer, bufsize, "ColorTest: (src.rgb & cmask)%s(dst.rgb & cmask) (extra %06x)", colorTests[data & 3], data & ~3);
|
||||
else
|
||||
snprintf(buffer, bufsize, "ColorTest: (src.rgb & cmask)%s(dst.rgb & cmask)", colorTests[data & 3]);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1060,18 +1224,21 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
break;
|
||||
|
||||
case GE_CMD_MASKRGB:
|
||||
snprintf(buffer, bufsize, "MaskRGB: %06x", data);
|
||||
snprintf(buffer, bufsize, "MaskRGB: %06x (bits not to write)", data);
|
||||
break;
|
||||
|
||||
case GE_CMD_MASKALPHA:
|
||||
snprintf(buffer, bufsize, "MaskAlpha: %06x", data);
|
||||
if (data & ~0xFF)
|
||||
snprintf(buffer, bufsize, "MaskAlpha: %02x (bits not to write) (extra %04x)", data & 0xFF, data >> 8);
|
||||
else
|
||||
snprintf(buffer, bufsize, "MaskAlpha: %02x (bits not to write)", data & 0xFF);
|
||||
break;
|
||||
|
||||
case GE_CMD_WORLDMATRIXNUMBER:
|
||||
if (data & ~0xF)
|
||||
snprintf(buffer, bufsize, "World # %i (extra %x)", data & 0xF, data);
|
||||
snprintf(buffer, bufsize, "World # %d (extra %x)", data & 0xF, data & ~0xF);
|
||||
else
|
||||
snprintf(buffer, bufsize, "World # %i", data & 0xF);
|
||||
snprintf(buffer, bufsize, "World # %d", data & 0xF);
|
||||
break;
|
||||
|
||||
case GE_CMD_WORLDMATRIXDATA:
|
||||
@@ -1080,9 +1247,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
|
||||
case GE_CMD_VIEWMATRIXNUMBER:
|
||||
if (data & ~0xF)
|
||||
snprintf(buffer, bufsize, "VIEW # %i (extra %x)", data & 0xF, data);
|
||||
snprintf(buffer, bufsize, "VIEW # %d (extra %x)", data & 0xF, data & ~0xF);
|
||||
else
|
||||
snprintf(buffer, bufsize, "VIEW # %i", data & 0xF);
|
||||
snprintf(buffer, bufsize, "VIEW # %d", data & 0xF);
|
||||
break;
|
||||
|
||||
case GE_CMD_VIEWMATRIXDATA:
|
||||
@@ -1091,9 +1258,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
|
||||
case GE_CMD_PROJMATRIXNUMBER:
|
||||
if (data & ~0xF)
|
||||
snprintf(buffer, bufsize, "PROJECTION # %i (extra %x)", data & 0xF, data);
|
||||
snprintf(buffer, bufsize, "PROJECTION # %d (extra %x)", data & 0xF, data & ~0xF);
|
||||
else
|
||||
snprintf(buffer, bufsize, "PROJECTION # %i", data & 0xF);
|
||||
snprintf(buffer, bufsize, "PROJECTION # %d", data & 0xF);
|
||||
break;
|
||||
|
||||
case GE_CMD_PROJMATRIXDATA:
|
||||
@@ -1102,9 +1269,9 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
|
||||
case GE_CMD_TGENMATRIXNUMBER:
|
||||
if (data & ~0xF)
|
||||
snprintf(buffer, bufsize, "TGEN # %i (extra %x)", data & 0xF, data);
|
||||
snprintf(buffer, bufsize, "TGEN # %d (extra %x)", data & 0xF, data & ~0xF);
|
||||
else
|
||||
snprintf(buffer, bufsize, "TGEN # %i", data & 0xF);
|
||||
snprintf(buffer, bufsize, "TGEN # %d", data & 0xF);
|
||||
break;
|
||||
|
||||
case GE_CMD_TGENMATRIXDATA:
|
||||
@@ -1113,13 +1280,13 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
|
||||
case GE_CMD_BONEMATRIXNUMBER:
|
||||
if (data & ~0x7F)
|
||||
snprintf(buffer, bufsize, "BONE #%i (extra %x)", data & 0x7F, data);
|
||||
snprintf(buffer, bufsize, "BONE #%d (extra %x)", data & 0x7F, data & ~0x7F);
|
||||
else
|
||||
snprintf(buffer, bufsize, "BONE #%i", data & 0x7F);
|
||||
snprintf(buffer, bufsize, "BONE #%d", data & 0x7F);
|
||||
break;
|
||||
|
||||
case GE_CMD_BONEMATRIXDATA:
|
||||
snprintf(buffer, bufsize, "BONE data #%i %f", gstate.boneMatrixNumber & 0x7f, getFloat24(data));
|
||||
snprintf(buffer, bufsize, "BONE data # %f", getFloat24(data));
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -813,7 +813,7 @@ void SoftGPU::Execute_BlockTransferStart(u32 op, u32 diff) {
|
||||
}
|
||||
|
||||
if (MemBlockInfoDetailed(srcSize, dstSize)) {
|
||||
const std::string tag = "GPUBlockTransfer/" + GetMemWriteTagAt(src, srcSize);
|
||||
const std::string tag = GetMemWriteTagAt("GPUBlockTransfer/", src, srcSize);
|
||||
NotifyMemInfo(MemBlockFlags::READ, src, srcSize, tag.c_str(), tag.size());
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, dst, dstSize, tag.c_str(), tag.size());
|
||||
}
|
||||
|
||||
@@ -787,7 +787,7 @@ void DrawEngineVulkan::DoFlush() {
|
||||
lastRenderStepId_ = curRenderStepId;
|
||||
}
|
||||
|
||||
renderManager->BindPipeline(pipeline->pipeline, (PipelineFlags)pipeline->flags);
|
||||
renderManager->BindPipeline(pipeline->pipeline, (PipelineFlags)pipeline->flags, pipelineLayout_);
|
||||
if (pipeline != lastPipeline_) {
|
||||
if (lastPipeline_ && !(lastPipeline_->UsesBlendConstant() && pipeline->UsesBlendConstant())) {
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE);
|
||||
@@ -814,9 +814,9 @@ void DrawEngineVulkan::DoFlush() {
|
||||
if (!ibuf) {
|
||||
ibOffset = (uint32_t)frameData.pushIndex->Push(decIndex, sizeof(uint16_t) * indexGen.VertexCount(), &ibuf);
|
||||
}
|
||||
renderManager->DrawIndexed(pipelineLayout_, ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, vertexCount, 1, VK_INDEX_TYPE_UINT16);
|
||||
renderManager->DrawIndexed(ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, vertexCount, 1, VK_INDEX_TYPE_UINT16);
|
||||
} else {
|
||||
renderManager->Draw(pipelineLayout_, ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, vertexCount);
|
||||
renderManager->Draw(ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, vertexCount);
|
||||
}
|
||||
} else {
|
||||
PROFILE_THIS_SCOPE("soft");
|
||||
@@ -917,7 +917,7 @@ void DrawEngineVulkan::DoFlush() {
|
||||
lastRenderStepId_ = curRenderStepId;
|
||||
}
|
||||
|
||||
renderManager->BindPipeline(pipeline->pipeline, (PipelineFlags)pipeline->flags);
|
||||
renderManager->BindPipeline(pipeline->pipeline, (PipelineFlags)pipeline->flags, pipelineLayout_);
|
||||
if (pipeline != lastPipeline_) {
|
||||
if (lastPipeline_ && !lastPipeline_->UsesBlendConstant() && pipeline->UsesBlendConstant()) {
|
||||
gstate_c.Dirty(DIRTY_BLEND_STATE);
|
||||
@@ -949,11 +949,11 @@ void DrawEngineVulkan::DoFlush() {
|
||||
VkBuffer vbuf, ibuf;
|
||||
vbOffset = (uint32_t)frameData.pushVertex->Push(result.drawBuffer, maxIndex * sizeof(TransformedVertex), &vbuf);
|
||||
ibOffset = (uint32_t)frameData.pushIndex->Push(inds, sizeof(short) * result.drawNumTrans, &ibuf);
|
||||
renderManager->DrawIndexed(pipelineLayout_, ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, result.drawNumTrans, 1, VK_INDEX_TYPE_UINT16);
|
||||
renderManager->DrawIndexed(ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, result.drawNumTrans, 1, VK_INDEX_TYPE_UINT16);
|
||||
} else {
|
||||
VkBuffer vbuf;
|
||||
vbOffset = (uint32_t)frameData.pushVertex->Push(result.drawBuffer, result.drawNumTrans * sizeof(TransformedVertex), &vbuf);
|
||||
renderManager->Draw(pipelineLayout_, ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, result.drawNumTrans);
|
||||
renderManager->Draw(ds, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, result.drawNumTrans);
|
||||
}
|
||||
} else if (result.action == SW_CLEAR) {
|
||||
// Note: we won't get here if the clear is alpha but not color, or color but not alpha.
|
||||
|
||||
@@ -136,7 +136,7 @@ void DrawEngineVulkan::ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManag
|
||||
bool useBufferedRendering = framebufferManager_->UseBufferedRendering();
|
||||
|
||||
if (gstate_c.IsDirty(DIRTY_BLEND_STATE)) {
|
||||
gstate_c.SetAllowFramebufferRead(!g_Config.bDisableSlowFramebufEffects);
|
||||
gstate_c.SetAllowFramebufferRead(!g_Config.bDisableShaderBlending);
|
||||
if (gstate.isModeClear()) {
|
||||
key.logicOpEnable = false;
|
||||
key.logicOp = VK_LOGIC_OP_CLEAR;
|
||||
|
||||
@@ -464,7 +464,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
|
||||
|
||||
// Any texture scaling is gonna move away from the original 16-bit format, if any.
|
||||
VkFormat actualFmt = plan.scaleFactor > 1 ? VULKAN_8888_FORMAT : dstFmt;
|
||||
if (plan.replaced->Valid()) {
|
||||
if (plan.replaceValid) {
|
||||
actualFmt = ToVulkanFormat(plan.replaced->Format(plan.baseLevelSrc));
|
||||
}
|
||||
|
||||
@@ -503,7 +503,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
|
||||
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.depth, plan.levelsToCreate, actualFmt, imageLayout, usage, mapping);
|
||||
bool allocSuccess = image->CreateDirect(cmdInit, plan.createW, plan.createH, plan.depth, 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;
|
||||
@@ -519,10 +519,15 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
|
||||
host->NotifyUserMessage(err->T("Warning: Video memory FULL, switching to slow caching mode"), 2.0f);
|
||||
}
|
||||
|
||||
// Turn off texture replacement for this texture.
|
||||
plan.replaced = &replacer_.FindNone();
|
||||
|
||||
plan.createW /= plan.scaleFactor;
|
||||
plan.createH /= plan.scaleFactor;
|
||||
plan.scaleFactor = 1;
|
||||
actualFmt = dstFmt;
|
||||
|
||||
allocSuccess = image->CreateDirect(cmdInit, plan.w * plan.scaleFactor, plan.h * plan.scaleFactor, plan.depth, plan.levelsToCreate, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping);
|
||||
allocSuccess = image->CreateDirect(cmdInit, plan.createW, plan.createH, plan.depth, plan.levelsToCreate, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping);
|
||||
}
|
||||
|
||||
if (!allocSuccess) {
|
||||
@@ -537,7 +542,8 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
|
||||
|
||||
ReplacedTextureDecodeInfo replacedInfo;
|
||||
bool willSaveTex = false;
|
||||
if (replacer_.Enabled() && plan.replaced->IsInvalid() && plan.depth == 1) {
|
||||
if (replacer_.Enabled() && !plan.replaceValid && plan.depth == 1) {
|
||||
// TODO: Do we handle the race where a replacement becomes valid AFTER this but before we save?
|
||||
replacedInfo.cachekey = entry->CacheKey();
|
||||
replacedInfo.hash = entry->fullhash;
|
||||
replacedInfo.addr = entry->addr;
|
||||
@@ -563,15 +569,14 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
|
||||
int mipUnscaledWidth = gstate.getTextureWidth(i);
|
||||
int mipUnscaledHeight = gstate.getTextureHeight(i);
|
||||
|
||||
int mipWidth = mipUnscaledWidth * plan.scaleFactor;
|
||||
int mipHeight = mipUnscaledHeight * plan.scaleFactor;
|
||||
if (plan.replaced->Valid()) {
|
||||
plan.replaced->GetSize(plan.baseLevelSrc + i, mipWidth, mipHeight);
|
||||
}
|
||||
int mipWidth;
|
||||
int mipHeight;
|
||||
plan.GetMipSize(i, &mipWidth, &mipHeight);
|
||||
|
||||
int bpp = actualFmt == VULKAN_8888_FORMAT ? 4 : 2; // output bpp
|
||||
int stride = (mipWidth * bpp + 15) & ~15; // output stride
|
||||
int size = stride * mipHeight;
|
||||
int uploadSize = 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.
|
||||
@@ -592,9 +597,9 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
|
||||
};
|
||||
|
||||
bool dataScaled = true;
|
||||
if (plan.replaced->Valid()) {
|
||||
if (plan.replaceValid) {
|
||||
// Directly load the replaced image.
|
||||
data = drawEngine_->GetPushBufferForTextureData()->PushAligned(size, &bufferOffset, &texBuf, pushAlignment);
|
||||
data = drawEngine_->GetPushBufferForTextureData()->PushAligned(uploadSize, &bufferOffset, &texBuf, pushAlignment);
|
||||
double replaceStart = time_now_d();
|
||||
plan.replaced->Load(plan.baseLevelSrc + i, data, stride); // if it fails, it'll just be garbage data... OK for now.
|
||||
replacementTimeThisFrame_ += time_now_d() - replaceStart;
|
||||
@@ -604,7 +609,8 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
|
||||
VK_PROFILE_END(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
} else {
|
||||
if (plan.depth != 1) {
|
||||
loadLevel(size, i, stride, plan.scaleFactor);
|
||||
// 3D texturing.
|
||||
loadLevel(uploadSize, i, stride, plan.scaleFactor);
|
||||
entry->vkTex->UploadMip(cmdInit, 0, mipWidth, mipHeight, i, texBuf, bufferOffset, stride / bpp);
|
||||
} else if (computeUpload) {
|
||||
int srcBpp = dstFmt == VULKAN_8888_FORMAT ? 4 : 2;
|
||||
@@ -626,7 +632,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
|
||||
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);
|
||||
loadLevel(uploadSize, i == 0 ? plan.baseLevelSrc : i, stride, plan.scaleFactor);
|
||||
VK_PROFILE_BEGIN(vulkan, cmdInit, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
"Copy Upload: %dx%d", mipWidth, mipHeight);
|
||||
entry->vkTex->UploadMip(cmdInit, i, mipWidth, mipHeight, 0, texBuf, bufferOffset, stride / bpp);
|
||||
@@ -663,7 +669,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
|
||||
entry->status |= TexCacheEntry::STATUS_3D;
|
||||
}
|
||||
|
||||
if (plan.replaced->Valid()) {
|
||||
if (plan.replaceValid) {
|
||||
entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus()));
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -405,7 +405,7 @@ enum GELightComputation
|
||||
GE_LIGHTCOMP_ONLYPOWDIFFUSE = 2,
|
||||
};
|
||||
|
||||
enum GETextureFormat
|
||||
enum GETextureFormat : uint8_t
|
||||
{
|
||||
GE_TFMT_5650 = 0,
|
||||
GE_TFMT_5551 = 1,
|
||||
|
||||
@@ -508,7 +508,7 @@ void GameSettingsScreen::CreateViews() {
|
||||
});
|
||||
texSecondary_->SetDisabledPtr(&g_Config.bSoftwareRendering);
|
||||
|
||||
CheckBox *framebufferSlowEffects = graphicsSettings->Add(new CheckBox(&g_Config.bDisableSlowFramebufEffects, gr->T("Disable slower effects (speedup)")));
|
||||
CheckBox *framebufferSlowEffects = graphicsSettings->Add(new CheckBox(&g_Config.bDisableShaderBlending, gr->T("Disable slower effects (speedup)")));
|
||||
framebufferSlowEffects->SetDisabledPtr(&g_Config.bSoftwareRendering);
|
||||
|
||||
// Seems solid, so we hide the setting.
|
||||
|
||||
+18
-8
@@ -287,13 +287,6 @@ UCKS45022 = true
|
||||
ULJS19009 = true
|
||||
NPJH50141 = true
|
||||
|
||||
[DisableReadbacks]
|
||||
# MotoGP copies the framebuffer to RAM every frame. We have a hack to display it directly,
|
||||
# which means we don't also need a readback.
|
||||
ULJS00078 = true
|
||||
ULUS10153 = true
|
||||
UCES00373 = true
|
||||
|
||||
[DrawSyncEatCycles]
|
||||
# This replaced Crash Tag Team Racing hack to also fix Gundam games
|
||||
# It makes sceGeDrawSync eat a lot of cycles which can affect timing in lots of games,
|
||||
@@ -535,7 +528,7 @@ ULJM08033 = true
|
||||
NPJH50373 = true
|
||||
NPUH10191 = true
|
||||
NPUH10197 = true
|
||||
# Grand Knights History need it to fix blackboxes on characters and flickering texture . See issues #2135 , #6099
|
||||
# Grand Knights History need it to fix blackboxes on characters and flickering texture . See issues #2135, #6099
|
||||
ULJS00394 = true
|
||||
ULJS19068 = true
|
||||
NPJH50518 = true
|
||||
@@ -1011,6 +1004,14 @@ ULUS10317 = true
|
||||
ULUS10598 = true
|
||||
ULES01578 = true
|
||||
|
||||
# Killzone: Liberation (see issue #6207)
|
||||
UCES00279 = true
|
||||
UCKS45041 = true
|
||||
UCUS98646 = true
|
||||
UCET00278 = true
|
||||
UCUS98670 = true
|
||||
UCUS98646 = true
|
||||
|
||||
[JitInvalidationHack]
|
||||
# This is an absolutely awful hack that somehow prevents issues when clearing the JIT,
|
||||
# if the game has copied code with EmuHack opcodes or something. Hopefully will be able
|
||||
@@ -1263,3 +1264,12 @@ UCKS45048 = true
|
||||
UCJS18030 = true
|
||||
UCJS18047 = true
|
||||
NPJG00015 = true
|
||||
|
||||
[SplitFramebufferMargin]
|
||||
# Killzone: Liberation (see issue #6207)
|
||||
UCES00279 = true
|
||||
UCKS45041 = true
|
||||
UCUS98646 = true
|
||||
UCET00278 = true
|
||||
UCUS98670 = true
|
||||
UCUS98646 = true
|
||||
@@ -379,7 +379,7 @@ class LibretroHost : public Host
|
||||
void InitSound() override {}
|
||||
void UpdateSound() override
|
||||
{
|
||||
extern int hostAttemptBlockSize;
|
||||
int hostAttemptBlockSize = __AudioGetHostAttemptBlockSize();
|
||||
const int blockSizeMax = 512;
|
||||
static int16_t audio[blockSizeMax * 2];
|
||||
assert(hostAttemptBlockSize <= blockSizeMax);
|
||||
@@ -563,7 +563,7 @@ static RetroOption<bool> ppsspp_lazy_texture_caching("ppsspp_lazy_texture_cachin
|
||||
static RetroOption<bool> ppsspp_retain_changed_textures("ppsspp_retain_changed_textures", "Retain changed textures (Speedup, mem hog)", false);
|
||||
static RetroOption<bool> ppsspp_force_lag_sync("ppsspp_force_lag_sync", "Force real clock sync (Slower, less lag)", false);
|
||||
static RetroOption<int> ppsspp_spline_quality("ppsspp_spline_quality", "Spline/Bezier curves quality", { {"Low", 0}, {"Medium", 1}, {"High", 2} });
|
||||
static RetroOption<bool> ppsspp_disable_slow_framebuffer_effects("ppsspp_disable_slow_framebuffer_effects", "Disable slower effects (Speedup)", false);
|
||||
static RetroOption<bool> ppsspp_disable_shader_blending("ppsspp_disable_slow_framebuffer_effects", "Disable shader blending (speedup)", false);
|
||||
static RetroOption<bool> ppsspp_enable_wlan("ppsspp_enable_wlan", "Enable Networking/WLAN (beta, may break games)", false);
|
||||
static RetroOption<std::string> ppsspp_change_mac_address[] = {
|
||||
{"ppsspp_change_mac_address01", "MAC address Pt 1: X-:--:--:--:--:--", MAC_INITIALIZER_LIST},
|
||||
@@ -699,7 +699,7 @@ void retro_set_environment(retro_environment_t cb)
|
||||
vars.push_back(ppsspp_lazy_texture_caching.GetOptions());
|
||||
vars.push_back(ppsspp_retain_changed_textures.GetOptions());
|
||||
vars.push_back(ppsspp_force_lag_sync.GetOptions());
|
||||
vars.push_back(ppsspp_disable_slow_framebuffer_effects.GetOptions());
|
||||
vars.push_back(ppsspp_disable_shader_blending.GetOptions());
|
||||
vars.push_back(ppsspp_lower_resolution_for_effects.GetOptions());
|
||||
vars.push_back(ppsspp_texture_scaling_level.GetOptions());
|
||||
vars.push_back(ppsspp_texture_scaling_type.GetOptions());
|
||||
@@ -831,7 +831,7 @@ static void check_variables(CoreParameter &coreParam)
|
||||
ppsspp_retain_changed_textures.Update(&g_Config.bTextureSecondaryCache);
|
||||
ppsspp_force_lag_sync.Update(&g_Config.bForceLagSync);
|
||||
ppsspp_spline_quality.Update(&g_Config.iSplineBezierQuality);
|
||||
ppsspp_disable_slow_framebuffer_effects.Update(&g_Config.bDisableSlowFramebufEffects);
|
||||
ppsspp_disable_shader_blending.Update(&g_Config.bDisableShaderBlending);
|
||||
ppsspp_inflight_frames.Update(&g_Config.iInflightFrames);
|
||||
const bool do_scaling_type_update = ppsspp_texture_scaling_type.Update(&g_Config.iTexScalingType);
|
||||
const bool do_scaling_level_update = ppsspp_texture_scaling_level.Update(&g_Config.iTexScalingLevel);
|
||||
|
||||
@@ -102,7 +102,7 @@ bool TestCompileShader(const char *buffer, ShaderLanguage lang, ShaderStage stag
|
||||
}
|
||||
case ShaderLanguage::HLSL_D3D9:
|
||||
{
|
||||
LPD3DBLOB blob = CompileShaderToByteCodeD3D9(buffer, stage == ShaderStage::Vertex ? "vs_2_0" : "ps_2_0", errorMessage);
|
||||
LPD3DBLOB blob = CompileShaderToByteCodeD3D9(buffer, stage == ShaderStage::Vertex ? "vs_3_0" : "ps_3_0", errorMessage);
|
||||
if (blob) {
|
||||
blob->Release();
|
||||
return true;
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <jni.h>
|
||||
#endif
|
||||
|
||||
#include "Common/Data/Collections/TinySet.h"
|
||||
#include "Common/Data/Text/Parsers.h"
|
||||
#include "Common/Data/Text/WrapText.h"
|
||||
#include "Common/Data/Encoding/Utf8.h"
|
||||
@@ -308,6 +309,44 @@ bool TestParsers() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TestTinySet() {
|
||||
TinySet<int, 4> a;
|
||||
EXPECT_EQ_INT((int)a.size(), 0);
|
||||
a.push_back(1);
|
||||
EXPECT_EQ_INT((int)a.size(), 1);
|
||||
a.push_back(2);
|
||||
EXPECT_EQ_INT((int)a.size(), 2);
|
||||
TinySet<int, 4> b;
|
||||
b.push_back(8);
|
||||
b.push_back(9);
|
||||
b.push_back(10);
|
||||
EXPECT_EQ_INT((int)b.size(), 3);
|
||||
|
||||
a.append(b);
|
||||
EXPECT_EQ_INT((int)a.size(), 5);
|
||||
EXPECT_EQ_INT((int)b.size(), 3);
|
||||
|
||||
b.append(b);
|
||||
EXPECT_EQ_INT((int)b.size(), 6);
|
||||
|
||||
EXPECT_EQ_INT(a[0], 1);
|
||||
EXPECT_EQ_INT(a[1], 2);
|
||||
EXPECT_EQ_INT(a[2], 8);
|
||||
EXPECT_EQ_INT(a[3], 9);
|
||||
EXPECT_EQ_INT(a[4], 10);
|
||||
a.append(a);
|
||||
EXPECT_EQ_INT(a.size(), 10);
|
||||
EXPECT_EQ_INT(a[9], 10);
|
||||
|
||||
b.push_back(11);
|
||||
EXPECT_EQ_INT((int)b.size(), 7);
|
||||
b.push_back(12);
|
||||
EXPECT_EQ_INT((int)b.size(), 8);
|
||||
b.push_back(13);
|
||||
EXPECT_EQ_INT(b.size(), 9);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TestVFPUSinCos() {
|
||||
float sine, cosine;
|
||||
InitVFPUSinCos();
|
||||
@@ -792,6 +831,7 @@ TestItem availableTests[] = {
|
||||
TEST_ITEM(AndroidContentURI),
|
||||
TEST_ITEM(ThreadManager),
|
||||
TEST_ITEM(WrapText),
|
||||
TEST_ITEM(TinySet),
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
|
||||
Reference in new issue
Block a user