Compare commits

...
Author SHA1 Message Date
nmzikandGitHub 85622befb8 Fix fabricated HTTP2 success (#129)
@StefanosCosta Thanks!
2026-07-30 00:48:08 +02:00
nmzik 3965d41d36 texture_cache: fix exact-match reuse across different tile modes 2026-07-30 00:10:40 +02:00
nmzik c508c4a9c0 shader_recompiler: allow GDS append/consume offsets 2026-07-30 00:10:40 +02:00
5 changed files with 161 additions and 56 deletions
+35 -16
View File
@@ -88,15 +88,34 @@ TextureCache::~TextureCache() {
bool TextureCache::SameBacking(const ImageInfo& cached, const ImageInfo& requested,
bool exact_format) {
const bool unit_extent =
requested.extent.width == 1 && requested.extent.height == 1 && requested.extent.depth == 1;
return cached.data == requested.data && cached.extent == requested.extent &&
cached.samples == requested.samples &&
cached.bytes_per_block == requested.bytes_per_block &&
(cached.type == requested.type || unit_extent) &&
(exact_format
? cached.pixel_format == requested.pixel_format
: ImageViewOps::FormatsCompatible(cached.pixel_format, requested.pixel_format));
if (cached.data.address != requested.data.address) {
return false;
}
if (cached.data.size != requested.data.size) {
return false;
}
if (cached.extent != requested.extent) {
return false;
}
if (cached.samples != requested.samples) {
return false;
}
if (cached.bytes_per_block != requested.bytes_per_block) {
return false;
}
if (cached.tile_mode != requested.tile_mode) {
return false;
}
if (!ImageViewOps::FormatsCompatible(cached.pixel_format, requested.pixel_format)) {
return false;
}
if (cached.type != requested.type && requested.extent != vk::Extent3D {1, 1, 1}) {
return false;
}
if (exact_format && cached.pixel_format != requested.pixel_format) {
return false;
}
return true;
}
TextureCache::BindingType TextureCache::UploadBinding(const Image& image) {
@@ -735,6 +754,12 @@ TextureCache::OverlapResult TextureCache::ResolveOverlap(const ImageInfo& reques
(requested.IsVolume() || cached.info.IsVolume())) {
return {ExpandImage(requested, cached_id)};
}
if (requested.tile_mode != cached.info.tile_mode) {
if (safe_to_delete) {
DeleteImages(std::array {cached_id}, cached_id);
}
return {merged_id};
}
if (requested.pixel_format != cached.info.pixel_format ||
requested.data.size <= cached.info.data.size) {
const auto result_id = merged_id ? merged_id : cached_id;
@@ -747,12 +772,6 @@ TextureCache::OverlapResult TextureCache::ResolveOverlap(const ImageInfo& reques
if (requested.type == cached.info.type && requested.resources > cached.info.resources) {
return {ExpandImage(requested, cached_id)};
}
if (requested.tile_mode != cached.info.tile_mode) {
if (safe_to_delete) {
DeleteImages(std::array {cached_id}, cached_id);
}
return {merged_id};
}
EXIT("TextureCache: unresolvable equal-address image overlap, address=0x%016" PRIx64
" requested=%ux%u "
"cached=%ux%u requested_size=0x%016" PRIx64 " cached_size=0x%016" PRIx64
@@ -1122,7 +1141,7 @@ ImageId TextureCache::FindImage(ImageDesc& desc, bool exact_format) {
for (const auto id: candidates) {
const auto owner = ResolveOwner(id);
if (owner == nullptr || owner->info.data != desc.info.data) {
if (owner == nullptr) {
continue;
}
if (SameBacking(owner->info, desc.info, exact_format)) {
@@ -421,13 +421,13 @@ bool DecodeDs(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
const uint32_t data0 = (word1 >> 8u) & 0xffu;
const uint32_t addr = word1 & 0xffu;
inst.pc = pc;
inst.word = word0;
inst.word_count = 2;
inst.offset = offset0 | (offset1 << 8u);
inst.gds = ((word0 >> 17u) & 1u) != 0u;
inst.family = Family::DS;
inst.opcode_id = opcode;
inst.pc = pc;
inst.word = word0;
inst.word_count = 2;
inst.offset = offset0 | (offset1 << 8u);
inst.gds = ((word0 >> 17u) & 1u) != 0u;
inst.family = Family::DS;
inst.opcode_id = opcode;
const auto* info = LookupMemoryOpcode(DS_OPS, static_cast<uint32_t>(std::size(DS_OPS)), opcode);
ApplyMemoryInfo(inst, info);
SetRawWords(inst, code, word_index, 2);
@@ -442,11 +442,6 @@ bool DecodeDs(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
inst.opcode == Opcode::DsReadAddtidB32)) {
SetUnsupported(inst, Family::DS, opcode, "DS swizzle/addtid is available only for LDS");
}
if (inst.gds && (inst.opcode == Opcode::DsAppend || inst.opcode == Opcode::DsConsume) &&
inst.offset != 0u) {
SetUnsupported(inst, Family::DS, opcode,
"GDS append/consume requires a zero instruction offset");
}
if (inst.opcode == Opcode::DsWriteAddtidB32 && data1 != 0u) {
SetUnsupported(inst, Family::DS, opcode,
"DS write addtid data1 operand is not implemented");
@@ -1,7 +1,7 @@
#include "graphics/shader/recompiler/emitter/SpirvEmitter.h"
#include "graphics/shader/recompiler/ir/SrtWalker.h"
#include "graphics/shader/recompiler/emitter/spirvEmitterInternal.h"
#include "graphics/shader/recompiler/ir/SrtWalker.h"
#include <algorithm>
#include <array>
@@ -160,8 +160,7 @@ bool ValidateInstructionContract(const IR::Instruction& inst, std::string* error
inst.dst.kind != IR::OperandKind::Null)) ||
((inst.op == IR::Opcode::DsAppend || inst.op == IR::Opcode::DsConsume) &&
(!ds_kind || !ds_resource || inst.src_count != 1 ||
inst.dst.kind != IR::OperandKind::Register ||
(kind == IR::ResourceKind::Gds && inst.memory.offset != 0))) ||
inst.dst.kind != IR::OperandKind::Register)) ||
((inst.op == IR::Opcode::DsMinF32 || inst.op == IR::Opcode::DsMaxF32) &&
(!ds_kind || !ds_resource || inst.src_count != 3 ||
inst.dst.kind != IR::OperandKind::Null)) ||
+54 -21
View File
@@ -654,6 +654,8 @@ namespace LibHttp2 {
LIB_VERSION("Http2", 1, "Http2", 1, 1);
constexpr int HTTP2_ERROR_INVALID_ID = -2122641152; /* 0x817B1100 */
constexpr int HTTP2_ERROR_BEFORE_SEND = -2122641307; /* 0x817B1065 */
constexpr int HTTP2_ERROR_TIMEOUT = -2122641304; /* 0x817B1068 */
constexpr int HTTP2_ERROR_NULL_POINTER = -2122640859; /* 0x817B1225 */
struct Http2Options {
@@ -694,14 +696,14 @@ struct Http2Request {
std::string url;
uint64_t content_length = 0;
std::vector<std::pair<std::string, std::string>> headers;
bool sent = false;
int status_code = 204;
std::string response_headers = "HTTP/2 204 No Content\r\n\r\n";
std::string response_body;
size_t read_offset = 0;
int async_result = 0;
int async_event = 0;
Http2Options options;
int send_result = HTTP2_ERROR_BEFORE_SEND;
int status_code = 0;
std::string response_headers;
std::string response_body;
size_t read_offset = 0;
int async_result = HTTP2_ERROR_BEFORE_SEND;
int async_event = 0;
Http2Options options;
};
struct Http2AsyncResult {
@@ -1114,9 +1116,9 @@ static int KYTY_SYSV_ABI Http2SendRequest(int req_id, const void* post_data, siz
return HTTP2_ERROR_INVALID_ID;
}
request->second.sent = true;
request->second.send_result = HTTP2_ERROR_TIMEOUT;
return 0;
return request->second.send_result;
}
static int KYTY_SYSV_ABI Http2SendRequestAsync(int req_id, const void* post_data, size_t size,
@@ -1136,8 +1138,8 @@ static int KYTY_SYSV_ABI Http2SendRequestAsync(int req_id, const void* post_data
return HTTP2_ERROR_INVALID_ID;
}
request->second.sent = true;
request->second.async_result = 0;
request->second.send_result = HTTP2_ERROR_TIMEOUT;
request->second.async_result = request->second.send_result;
request->second.async_event = 0;
return 0;
@@ -1159,11 +1161,10 @@ static int KYTY_SYSV_ABI Http2WaitAsync(int req_id, Http2AsyncResult* result, ui
return HTTP2_ERROR_INVALID_ID;
}
request->second.sent = true;
*result = {};
result->event_type = request->second.async_event;
result->req_id = req_id;
result->result = request->second.async_result;
*result = {};
result->event_type = request->second.async_event;
result->req_id = req_id;
result->result = request->second.async_result;
return 0;
}
@@ -1178,13 +1179,19 @@ static int KYTY_SYSV_ABI Http2GetStatusCode(int req_id, int* status_code) {
return HTTP2_ERROR_NULL_POINTER;
}
*status_code = 0;
auto request = g_http2_requests.find(req_id);
if (request == g_http2_requests.end()) {
return HTTP2_ERROR_INVALID_ID;
}
*status_code = request->second.status_code;
const int send_result = request->second.send_result;
if (send_result != 0) {
return send_result;
}
*status_code = request->second.status_code;
return 0;
}
@@ -1200,14 +1207,21 @@ static int KYTY_SYSV_ABI Http2GetResponseContentLength(int req_id, int* result,
return HTTP2_ERROR_NULL_POINTER;
}
*result = 0;
*content_length = 0;
auto request = g_http2_requests.find(req_id);
if (request == g_http2_requests.end()) {
return HTTP2_ERROR_INVALID_ID;
}
*result = 0; // SCE_HTTP2_CONTENTLEN_EXIST
*content_length = request->second.response_body.size();
const int send_result = request->second.send_result;
if (send_result != 0) {
*result = -1;
return send_result;
}
*content_length = request->second.response_body.size();
return 0;
}
@@ -1223,14 +1237,21 @@ static int KYTY_SYSV_ABI Http2GetAllResponseHeaders(int req_id, char** header,
return HTTP2_ERROR_NULL_POINTER;
}
*header = nullptr;
*header_size = 0;
auto request = g_http2_requests.find(req_id);
if (request == g_http2_requests.end()) {
return HTTP2_ERROR_INVALID_ID;
}
const int send_result = request->second.send_result;
if (send_result != 0) {
return send_result;
}
*header = const_cast<char*>(request->second.response_headers.c_str());
*header_size = request->second.response_headers.size();
return 0;
}
@@ -1250,6 +1271,11 @@ static int KYTY_SYSV_ABI Http2ReadData(int req_id, void* data, size_t size) {
return HTTP2_ERROR_INVALID_ID;
}
const int send_result = request->second.send_result;
if (send_result != 0) {
return send_result;
}
const auto& body = request->second.response_body;
const auto remaining =
request->second.read_offset < body.size() ? body.size() - request->second.read_offset : 0;
@@ -1280,6 +1306,13 @@ static int KYTY_SYSV_ABI Http2ReadDataAsync(int req_id, void* data, size_t size,
return HTTP2_ERROR_INVALID_ID;
}
const int send_result = request->second.send_result;
if (send_result != 0) {
request->second.async_result = send_result;
request->second.async_event = 1;
return 0;
}
const auto& body = request->second.response_body;
const auto remaining =
request->second.read_offset < body.size() ? body.size() - request->second.read_offset : 0;
+63 -4
View File
@@ -4772,6 +4772,52 @@ public:
"successive near-capacity image transfers replaced the shared "
"download buffer");
constexpr uint64_t tile_alias_offset = 0x2000000;
constexpr uint64_t tile_alias_size = 0x400000;
constexpr uint32_t tile_alias_extent = 1024;
std::memset(memory + tile_alias_offset, 0,
static_cast<size_t>(tile_alias_size));
auto render_target_alias = MakeLinearDesc(
base + tile_alias_offset, tile_alias_size,
vk::Format::eR8G8B8A8Unorm,
Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8UNorm),
Prospero::ImageType::kColor2D,
{tile_alias_extent, tile_alias_extent, 1}, 1, 4, 1);
render_target_alias.type = BindingType::Storage;
render_target_alias.info.tile_mode =
Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget);
render_target_alias.view_info.usage =
vk::ImageUsageFlagBits::eStorage;
const auto render_target_alias_image =
texture_cache.FindImage(render_target_alias);
auto standard_4kb_alias = render_target_alias;
standard_4kb_alias.type = BindingType::Texture;
standard_4kb_alias.info.tile_mode =
Prospero::GpuEnumValue(Prospero::TileMode::kStandard4KB);
standard_4kb_alias.view_info.usage =
vk::ImageUsageFlagBits::eSampled;
const auto standard_4kb_alias_image =
texture_cache.FindImage(standard_4kb_alias);
auto repeated_standard_4kb_alias = standard_4kb_alias;
const auto repeated_standard_4kb_alias_image =
texture_cache.FindImage(repeated_standard_4kb_alias);
Require(
name, "equal-size tile-mode alias",
render_target_alias_image && standard_4kb_alias_image &&
standard_4kb_alias_image != render_target_alias_image &&
repeated_standard_4kb_alias_image ==
standard_4kb_alias_image &&
texture_cache.GetImage(render_target_alias_image)
.info.tile_mode ==
Prospero::GpuEnumValue(
Prospero::TileMode::kRenderTarget) &&
texture_cache.GetImage(standard_4kb_alias_image)
.info.tile_mode ==
Prospero::GpuEnumValue(
Prospero::TileMode::kStandard4KB),
"equal address/size lookup reused an incompatible tiled backing");
for (auto &output : ms_observer_outputs) {
DestroyBuffer(&output);
}
@@ -13593,23 +13639,36 @@ TestCase DsAppendUsesEncodedGdsSelector() {
using O = ShaderOpcode;
std::vector<u32> code;
AppendSMovLiteral(&code, 124, 0x00000001u);
AppendSMovLiteral(&code, 124, 0x00000008u);
code.push_back(EncodeDs0(0x3e, 0, true));
code.push_back(EncodeDs1(0, 0, 0));
code.push_back(EncodeDs0(0x3d, 0, true));
code.push_back(EncodeDs1(1, 0, 0));
code.push_back(EncodeDs0(0x3e, 4, true));
code.push_back(EncodeDs1(2, 0, 0));
code.push_back(EncodeDs0(0x3d, 4, true));
code.push_back(EncodeDs1(3, 0, 0));
AppendSMovLiteral(&code, 124, 0x00080008u);
code.push_back(EncodeDs0(0x3e, 4, true));
code.push_back(EncodeDs1(4, 0, 0));
code.push_back(EncodeDs0(0x3d, 4, true));
code.push_back(EncodeDs1(5, 0, 0));
AppendStoreVgpr(&code, 0, 0);
AppendStoreVgpr(&code, 1, 1);
AppendStoreVgpr(&code, 2, 2);
AppendStoreVgpr(&code, 3, 3);
AppendStoreVgpr(&code, 4, 4);
AppendStoreVgpr(&code, 5, 5);
AppendEnd(&code);
TestCase test{
"DsAppendGdsSelector",
code,
{},
{10, 74},
{10, 74, 20, 84, 40, 104},
{O::SMovB32, O::DsAppend, O::DsConsume, O::BufferStoreDword, O::SEndpgm}};
test.gds_initial = {10};
test.expected_gds = {10};
test.gds_initial = {10, 20, 30, 40};
test.expected_gds = {10, 20, 30, 40};
return test;
}