Merge branch 'master' into feature_openxr_stereo

This commit is contained in:
Lubos committed 2022-09-01 18:09:21 +02:00
commit 00aaf9e1a6
62 files changed
+1034 -550

No files matched your search

+58 -1
View File
@@ -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) {
+2 -2
View File
@@ -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);
+1 -5
View File
@@ -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) {
+1
View File
@@ -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
+19 -7
View File
@@ -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");
+8 -8
View File
@@ -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 {
+2 -1
View File
@@ -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;
}
}
+8 -8
View File
@@ -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;
+5 -5
View File
@@ -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_);
}
}
+42
View File
@@ -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";
+4 -3
View File
@@ -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) {
+3 -3
View File
@@ -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);
+1 -1
View File
@@ -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) {
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+34 -20
View File
@@ -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() {
+3 -1
View File
@@ -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();
+1 -1
View File
@@ -129,6 +129,6 @@ protected:
private:
DialogStatus status = SCE_UTILITY_STATUS_NONE;
UtilityDialogType dialogType_;
UtilityDialogType dialogType_ = UtilityDialogType::NONE;
bool volatileLocked_ = false;
};
-3
View File
@@ -285,9 +285,6 @@ struct SaveFileInfo
PPGeImage *texture = nullptr;
SaveFileInfo() {
}
void DoState(PointerWrap &p);
};
+5 -5
View File
@@ -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
View File
@@ -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;
}
+2
View File
@@ -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();
+1 -1
View File
@@ -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 {
+1 -1
View File
@@ -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());
}
+4 -1
View File
@@ -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);
+1 -1
View File
@@ -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");
+3 -3
View File
@@ -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());
}
+3 -1
View File
@@ -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;
+3 -2
View File
@@ -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);
+2 -3
View File
@@ -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 {
+1 -1
View File
@@ -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 };
+13 -10
View File
@@ -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.
+149 -91
View File
@@ -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 &params, u32 skipDrawReason) {
static void ApplyKillzoneFramebufferSplit(FramebufferHeuristicParams *params, int *drawing_width);
VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(FramebufferHeuristicParams &params, 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(&params, &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;
}
}
+16 -15
View File
@@ -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 &params, u32 skipDrawReason);
VirtualFramebuffer *DoSetRenderFrameBuffer(FramebufferHeuristicParams &params, 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);
+8 -2
View File
@@ -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;
+7
View File
@@ -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;
}
+99 -62
View File
@@ -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) {
+25 -10
View File
@@ -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);
-3
View File
@@ -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);
+10 -10
View File
@@ -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
+1 -1
View File
@@ -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;
+25 -28
View File
@@ -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()));
}
}
+4 -8
View File
@@ -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;
+1 -1
View File
@@ -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);
+1 -2
View File
@@ -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
+12 -9
View File
@@ -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()));
}
}
+1 -1
View File
@@ -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
+14 -15
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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:
+1 -1
View File
@@ -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());
}
+6 -6
View File
@@ -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.
+1 -1
View File
@@ -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;
+21 -15
View File
@@ -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
View File
@@ -405,7 +405,7 @@ enum GELightComputation
GE_LIGHTCOMP_ONLYPOWDIFFUSE = 2,
};
enum GETextureFormat
enum GETextureFormat : uint8_t
{
GE_TFMT_5650 = 0,
GE_TFMT_5551 = 1,
+1 -1
View File
@@ -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
View File
@@ -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
+4 -4
View File
@@ -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);
+1 -1
View File
@@ -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
View File
@@ -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[]) {