Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
652a247
ci(macos): name the pull request's head commit in the package's COMMIT
claude Oct 11, 2026
dde80ad
fix(macos): stop the app packager on a missing param.json and never l…
claude Oct 11, 2026
6d409af
feat(agc-driver): trace multisampling state under APS5_TRACE_MSAA=1
claude Oct 11, 2026
f7aa9f8
feat(tools): unpack a fake-signed SELF into the ELF the relinker reads
claude Oct 11, 2026
66df77c
docs(macos): record DOOM in game on an M5 Pro and the first blockers …
claude Oct 11, 2026
3c1fc5a
docs(macos): name the package DOOM was tested with
claude Oct 11, 2026
2b74f73
feat(macos): convert a title folder to an app in one command
claude Oct 11, 2026
9568778
feat(agc-driver): render multisampled targets with one sample under A…
claude Oct 11, 2026
b75bb71
docs: describe APS5_MSAA_SINGLE_SAMPLE and APS5_TRACE_MSAA, and setti…
claude Oct 11, 2026
115a7f5
fix(macos): convert-ps5.sh finds every folder that holds modules
claude Oct 11, 2026
517400e
fix(macos): convert-ps5.sh moves modules in the title folder into a s…
claude Oct 11, 2026
832fcc8
fix(agc-driver): reject sample-mask exports of multisampled draws und…
claude Oct 11, 2026
5c96c97
fix(macos): convert-ps5.sh links moved root modules back at their /ap…
claude Oct 11, 2026
5fa9ebd
fix(agc-driver): name the reason a CB resolve copy was refused
claude Oct 11, 2026
361fdd5
test(agc-driver): check a CB resolve's DCC keys before the flush and …
claude Oct 11, 2026
252f8c5
fix(agc-driver): size the stencil replacement message for any mask value
claude Oct 11, 2026
6832c6c
fix(agc-driver): trace multisampled texture descriptors when their sh…
claude Oct 11, 2026
dbfd973
feat(macos): clone a title into its app and drop the quarantine attri…
claude Oct 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .github/workflows/macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,16 @@ jobs:
if: ${{ !cancelled() && steps.libs.outcome == 'success' }}
env:
VULKAN_SDK_DIR: ${{ steps.vulkan.outputs.dir }}
# A pull request builds GitHub's merge of its head into the base branch; COMMIT names the head first.
SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
BASE_REF: ${{ github.base_ref }}
run: |
set -euo pipefail
out=dist/anyps5-macos-x86_64
mkdir -p "$out/libs" "$out/docs"
cp build/core/relinker/relinker "$out/"
cp build/core/libs/libs/*.prx "$out/libs/"
cp tools/package_macos_app.py "$out/"
cp tools/package_macos_app.py tools/unfself.py tools/convert-ps5.sh "$out/"
cp docs/user/MACOS.md docs/user/USAGE.md docs/user/INPUT_MAPPING.md docs/user/COMPATIBILITY.md "$out/docs/"
if [ -n "$VULKAN_SDK_DIR" ]; then
mkdir -p "$out/vulkan/lib" "$out/vulkan/share/vulkan/icd.d" "$out/vulkan/licenses"
Expand All @@ -143,7 +146,9 @@ jobs:
find "$(dirname "$VULKAN_SDK_DIR")" -maxdepth 3 -iname '*licen[cs]e*' -type f -exec cp {} "$out/vulkan/licenses/" \; || true
printf '%s\n' "libvulkan.1.dylib (Khronos Vulkan-Loader) and libMoltenVK.dylib (KhronosGroup/MoltenVK) from the LunarG Vulkan SDK, both under the Apache License 2.0." > "$out/vulkan/README.txt"
fi
git rev-parse HEAD > "$out/COMMIT"
printf '%s\n' "$SOURCE_SHA" > "$out/COMMIT"
built=$(git rev-parse HEAD)
[ "$built" = "$SOURCE_SHA" ] || printf 'built as %s, its merge into %s\n' "$built" "$BASE_REF" >> "$out/COMMIT"
file "$out/relinker" "$out/libs/libkernel.prx"
tar -czf dist/anyps5-macos-x86_64.tar.gz -C dist anyps5-macos-x86_64
- uses: actions/upload-artifact@v7
Expand Down
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ if(BUILD_TESTING)
add_test(NAME package_macos_app COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_package_macos_app.py)
endif()
add_test(NAME unfself COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_unfself.py)
add_test(NAME hardware_oracle COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/hw-oracle/test_hw_oracle.py)
add_test(NAME pr_overlap_exports COMMAND ${Python3_EXECUTABLE}
Expand Down
16 changes: 14 additions & 2 deletions core/libs/prx/libSceAgcDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,12 @@ if(BUILD_TESTING)
set_tests_properties(agc_driver_gated_dispatch_capture_short_timeout PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
set_tests_properties(agc_driver_multisample_trace_capture PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_TRACE_MSAA=1")
add_test(NAME agc_driver_multisample_trace_capture_draw COMMAND agc_driver_multisample_trace_capture_tests draw)
set_tests_properties(agc_driver_multisample_trace_capture_draw PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_TRACE_MSAA=1" SKIP_RETURN_CODE 77 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
if(WIN32)
set_tests_properties(agc_driver_multisample_trace_capture_draw PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
set_tests_properties(agc_driver_provoking_vertex PROPERTIES TIMEOUT 30)
set_tests_properties(agc_driver_flat_store PROPERTIES RUN_SERIAL TRUE)
set_target_properties(agc_driver_flat_apertures_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
Expand All @@ -1040,8 +1046,14 @@ if(BUILD_TESTING)
add_test(NAME agc_driver_pm4_failure COMMAND agc_driver_pm4_tests failure)
set_tests_properties(agc_driver_pm4_failure PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77)
add_test(NAME agc_descriptor_pool_reset_failure COMMAND agc_driver_graphics_tests --descriptor-pool-reset-failure)
if(WIN32)
set_tests_properties(agc_driver_pm4_failure agc_descriptor_pool_reset_failure PROPERTIES ENVIRONMENT_MODIFICATION
add_test(NAME agc_driver_graphics_single_sample_msaa COMMAND agc_driver_graphics_tests --single-sample-msaa)
set_tests_properties(agc_driver_graphics_single_sample_msaa PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_MSAA_SINGLE_SAMPLE=1")
add_test(NAME agc_driver_graphics_msaa_trace COMMAND agc_driver_graphics_tests --msaa-trace)
set_tests_properties(agc_driver_graphics_msaa_trace PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_TRACE_MSAA=1")
add_test(NAME agc_driver_cmask_fast_clear_unimported COMMAND agc_driver_cmask_fast_clear_tests multisample)
set_tests_properties(agc_driver_cmask_fast_clear_unimported PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_NO_HOST_IMPORT=1" SKIP_RETURN_CODE 77 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
if(WIN32)
set_tests_properties(agc_driver_pm4_failure agc_descriptor_pool_reset_failure agc_driver_graphics_single_sample_msaa agc_driver_graphics_msaa_trace agc_driver_cmask_fast_clear_unimported PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
if(NOT WIN32 AND NOT APPLE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include "prx/libSceAgcDriver/Execution/include/Pm4.hpp"
#include "prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp"
#include "prx/libSceAgcDriver/Graphics/include/State.hpp"
#include "Optimization/ResourceProgram.hpp"
#include <algorithm>
#include <cstdlib>
#include <span>
#include <stdexcept>

namespace AgcDriver::DriverDetail {
Expand Down Expand Up @@ -250,6 +252,9 @@ void Driver::dispatch(QueueState& queue, std::span<const std::uint32_t> packet,
const SampledReadScope sampling(evidenceReads);
return shaderMemory->Capture(invocation);
}();
if (Graphics::TraceMultisampling()) {
for (const auto& image : capture->snapshot.images) Graphics::TraceMultisampleDescriptor(std::span(image.dwords).first(std::min<std::size_t>(image.dwordCount, image.dwords.size())), address);
}
captured = shaderMemory->Regions();
request.context.memory = captured;
if (traceCapSync()) traceCapture("dispatch-capture", address, submission.queue, captured, Graphics::Recorder::ThreadWaitedMs() - waitedBefore);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
#include "prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp"
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include "prx/libSceAgcDriver/Execution/include/Pm4.hpp"
#include "prx/libSceAgcDriver/Graphics/include/State.hpp"
#include "Optimization/ResourceProgram.hpp"
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <span>

namespace AgcDriver::DriverDetail {

Expand All @@ -33,6 +36,9 @@ ShaderRecompiler::RecompileResult Driver::materializeDrawStage(std::size_t i, st
const SampledReadScope sampling(evidenceReads);
return shaderMemory.Capture(invocation);
}();
if (Graphics::TraceMultisampling()) {
for (const auto& image : capture->snapshot.images) Graphics::TraceMultisampleDescriptor(std::span(image.dwords).first(std::min<std::size_t>(image.dwordCount, image.dwords.size())), program.binary.codeAddress);
}

stageCapture.regions = shaderMemory.TakeRecentRegions();
recompiled[i] = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ bool Driver::drawPrecheck() {

std::optional<DrawVerdict> Driver::precheckDraw(const QueueState& queue, const Submission& submission, std::span<const std::uint32_t> packet, const Pm4::DrawParameters& drawParameters, std::string& rejected, bool& traceIndirect) {
if (!drawParameters.indirect && !drawParameters.indexed && (drawParameters.indexCount == 0 || drawParameters.instanceCount == 0)) return DrawVerdict::Nothing;
Graphics::TraceMultisampleState(queue);
if (const auto pass = Graphics::DecodeColorMetadataPass(queue)) {
require(!drawParameters.indirect, "indirect CB metadata passes are unsupported");
GuestMemory::TagGpuLockSite(GuestMemory::GpuLockSite::Draw);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include <algorithm>
#include "prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp"
#include "prx/libSceAgcDriver/Graphics/include/State.hpp"
#include <bit>
#include <cstdlib>
#include <optional>
Expand Down Expand Up @@ -265,7 +266,10 @@ bool Driver::fillBuffer(QueueState& queue, std::uint32_t queueId, std::span<cons
for (std::size_t done = 0; done < bytes; done += chunk) GuestMemory::Write(base + done, std::span<const std::byte>(block).first(std::min(chunk, bytes - done)), 16);
phase(FillCpu);
}
if (pattern[0] == pattern[1] && pattern[1] == pattern[2] && pattern[2] == pattern[3]) Graphics::NoteDepthMetadataFill(base, bytes, pattern[0]);
if (pattern[0] == pattern[1] && pattern[1] == pattern[2] && pattern[2] == pattern[3]) {
Graphics::NoteDepthMetadataFill(base, bytes, pattern[0]);
Graphics::TraceMultisampleFill(base, bytes, pattern[0], "buffer fill");
}
if (profile) {
const auto now = std::chrono::steady_clock::now();
const auto cover = static_cast<std::size_t>(coverage.cover);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp"
#include "prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Recorder.hpp"
#include "prx/libSceAgcDriver/Graphics/include/State.hpp"
#include <cstdlib>
#include <cstring>
#include <limits>
Expand Down Expand Up @@ -60,7 +61,11 @@ bool Driver::preparePacketMemory(const Submission& submission, QueueState& queue
}
}
if (opcode == 0x50 && packet.size() >= 7) {
if (Pm4::DmaSource(packet) == 2 && Pm4::DmaDestination(packet) != 1) Graphics::NoteDepthMetadataFill(packet[4] | (static_cast<std::uint64_t>(packet[5]) << 32u), packet[6] & 0x3ffffffu, packet[2]);
if (Pm4::DmaSource(packet) == 2 && Pm4::DmaDestination(packet) != 1) {
const auto address = packet[4] | (static_cast<std::uint64_t>(packet[5]) << 32u);
Graphics::NoteDepthMetadataFill(address, packet[6] & 0x3ffffffu, packet[2]);
Graphics::TraceMultisampleFill(address, packet[6] & 0x3ffffffu, packet[2], "DMA_DATA fill");
}
}
if (!drainAll && !endOfPipeInterrupt && (opcode == 0x49 || opcode == 0x37)) {
if (const auto label = Pm4::DecodeLabelWrite(packet)) {
Expand Down
17 changes: 16 additions & 1 deletion core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <cstddef>
#include <cstdint>
#include <optional>
#include <span>
#include <string>
#include "Recompiler.hpp"

Expand Down Expand Up @@ -57,6 +58,7 @@ struct ColorTarget {
std::uint32_t exportIndex = 0;
bool uintExport = false;
std::uint32_t pipeBankXor = 0;
std::uint32_t samples = 1;
};

struct DepthTarget {
Expand All @@ -68,6 +70,7 @@ struct DepthTarget {
std::uint8_t clearStencil;
std::uint64_t htileAddress = 0;
bool htileStencil = false;
std::uint32_t samples = 1;
};

struct State {
Expand Down Expand Up @@ -120,9 +123,10 @@ std::size_t CmaskBytes(std::uint32_t width, std::uint32_t height);
std::uint32_t ColorWriteMask(const Registers& context);

struct ColorMetadataPass {
enum class Mode { EliminateFastClear, DccDecompress };
enum class Mode { EliminateFastClear, DccDecompress, Resolve, FmaskDecompress };
Mode mode;
std::vector<ColorTarget> targets;
std::optional<ColorTarget> source;
};
std::optional<ColorMetadataPass> DecodeColorMetadataPass(const QueueState& queue);
std::string DepthMaintenanceRejection(const QueueState& queue);
Expand All @@ -132,6 +136,17 @@ std::string DepthMaintenanceRejection(const QueueState& queue);
std::string DrawRejection(const QueueState& queue, bool indexed);
bool PixelProgramSkipped(const QueueState& queue);
std::string NullPixelProgramRejection(const QueueState& queue);
bool SingleSampleMultisamplingRequested(const char* value);
bool SingleSampleMultisampling();
bool MultisampleTraceRequested(const char* value);
bool TraceMultisampling();
std::vector<std::string>*& MultisampleTraceLog();
std::optional<std::string> MultisampleStateLine(const QueueState& queue);
std::optional<std::string> MultisampleDescriptorLine(std::span<const std::uint32_t> words, std::uint64_t program);
std::optional<std::string> MultisampleFillLine(std::uint64_t address, std::size_t bytes, std::uint32_t value, const char* source);
void TraceMultisampleState(const QueueState& queue);
void TraceMultisampleDescriptor(std::span<const std::uint32_t> words, std::uint64_t program);
void TraceMultisampleFill(std::uint64_t address, std::size_t bytes, std::uint32_t value, const char* source);

// The recording facade of the draw decoders (design_cpu_final M8, step 8a): every register read
// of DecodeShaderStages, DecodeState, DrawRejection, DecodePixelStageInfo and Driver::draw's
Expand Down
2 changes: 1 addition & 1 deletion core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ class StorageTexture : public std::enable_shared_from_this<StorageTexture> {
// hook), and the source's pending results stay pending. The shapes must match, neither surface
// may carry DCC metadata, and this surface must be host-imported (the write-back is GPU-direct).
// False, naming why, when the copy must be a transfer.
bool CopyFrom(StorageTexture& source, const char*& refusal);
bool CopyFrom(StorageTexture& source, const char*& refusal, bool resolve = false);
VkImage Image() const { return image; }
const GuestTextureResource& Descriptor() const { return descriptor; }
std::uint32_t ImageLayers() const { return geometry.imageLayers; }
Expand Down
43 changes: 41 additions & 2 deletions core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,25 @@ std::string hexAddress(std::uint64_t address) {
return text;
}

CmaskState multisampleCmask(const ColorTarget& color, std::span<const std::byte> mask) {
std::uint32_t colorCodes = 0;
for (const auto entry : mask) {
const auto value = std::to_integer<std::uint32_t>(entry);
colorCodes |= (1u << ((value >> 2u) & 3u)) | (1u << ((value >> 6u) & 3u));
}
Require((colorCodes & 6u) == 0, "CMASK color codes 1 and 2 of multisampled color target " + hexAddress(color.address) + " are not modeled (0 cleared, 3 expanded)");
Require(colorCodes != 9u, "the CMASK of multisampled color target " + hexAddress(color.address) + " fast-clears part of it, whose multisampled CMASK tile order is not modeled");
return colorCodes == 1u ? CmaskState::Cleared : CmaskState::Expanded;
}

void reportMultisampleTarget(std::uint64_t address, const char* what, std::uint32_t samples) {
static std::mutex mutex;
static std::set<std::uint64_t> reported;
std::lock_guard lock(mutex);
if (reported.size() >= 64 || !reported.insert(address).second) return;
std::fprintf(stderr, "[gpu] %ux multisampled %s 0x%llx is rendered with one sample (APS5_MSAA_SINGLE_SAMPLE)\n", samples, what, static_cast<unsigned long long>(address));
}

void requireCmaskCodes(std::uint32_t codes) {
if (codes == 0) return;
Require((codes & 1u) == 0, "a CMASK fast clear of a DCC color target, whose clear belongs in its DCC keys, was recorded");
Expand Down Expand Up @@ -239,6 +258,7 @@ CmaskState readCmask(const ColorTarget& color, const CmaskLayout& layout, std::v
}
std::vector<std::byte> mask(bytes);
GuestMemory::Read(address, mask, CmaskLayout::Alignment);
if (color.samples > 1u) return multisampleCmask(color, mask);
const auto uniform = [&](std::byte value) { return std::all_of(mask.begin(), mask.end(), [&](std::byte entry) { return entry == value; }); };
if (uniform(std::byte{0xff})) return CmaskState::Expanded;
if (uniform(std::byte{0})) return CmaskState::Cleared;
Expand Down Expand Up @@ -297,7 +317,7 @@ bool materializeCmaskClear(const Context& context, const ColorTarget& color, con
Require(color.cmaskBytes == bytes, "CMASK size differs from the color target's CMASK layout");
if (cmaskProved(color.cmaskAddress, bytes)) return false;
auto* recorder = Recorder::Active();
if (resident != nullptr && context.detiler != nullptr && recorder != nullptr && GuestMemory::GpuMutex().HeldByThisThread()) {
if (color.samples == 1u && resident != nullptr && context.detiler != nullptr && recorder != nullptr && GuestMemory::GpuMutex().HeldByThisThread()) {
const auto* import = HostImportFor(context, color.cmaskAddress, bytes);
const auto view = import != nullptr ? resident->ElementView() : VK_NULL_HANDLE;
if (view != VK_NULL_HANDLE) {
Expand All @@ -320,7 +340,7 @@ bool materializeCmaskClear(const Context& context, const ColorTarget& color, con
storeCmaskClearTexels(color, layout, state == CmaskState::Cleared ? nullptr : &cleared, texel);
if (resident != nullptr) resident->Refresh();
}
const std::vector<std::byte> expanded(bytes, std::byte{0xff});
const std::vector<std::byte> expanded(bytes, static_cast<std::byte>(color.samples > 1u ? 0xccu : 0xffu));
GuestMemory::Write(color.cmaskAddress, expanded, CmaskLayout::Alignment);
proveCmask(color.cmaskAddress, bytes, GuestMemory::CollectWrites(color.cmaskAddress, bytes));
return filled;
Expand Down Expand Up @@ -1867,6 +1887,7 @@ void Draw(const Context& context, const State& state, const Pm4::DrawParameters&
auto& binding = targets[index];
binding.color = state.colors[index];
const auto& color = binding.color;
if (color.samples > 1u) reportMultisampleTarget(color.address, "color target", color.samples);
binding.gpuTiling = color.tileMode == ColorTileMode::RenderTarget && context.detiler != nullptr;
APS5_LOG_OUT_DEBUG("Creating color target %zu address=0x%llx bytes=%llu extent=%ux%u", index, static_cast<unsigned long long>(color.address), static_cast<unsigned long long>(color.bytes), color.extent.width, color.extent.height);
const ColorTargetLayout colorLayout(color.extent.width, color.extent.height, color.tileMode, color.elementBytes);
Expand Down Expand Up @@ -1929,6 +1950,7 @@ void Draw(const Context& context, const State& state, const Pm4::DrawParameters&
binding.target = std::make_unique<RenderTarget>(context, color, state.blends.at(color.exportIndex).blendEnable != 0);
targetViews.push_back(binding.target->View());
}
if (state.depth && state.depth->samples > 1u) reportMultisampleTarget(state.depth->address != 0 ? state.depth->address : state.depth->stencilAddress, "depth target", state.depth->samples);
if (state.depth) targetViews.push_back(DepthSurfaceView(context, *state.depth));
timer.phase(PhasePrepare);
const bool recordDraws = RecordDraws();
Expand Down Expand Up @@ -2475,9 +2497,26 @@ std::shared_ptr<StorageTexture> metadataPassResident(const Context& context, con
return resident != nullptr && resident->GuestBytes() == color.bytes ? resident : nullptr;
}

void resolveColorTarget(const Context& context, const ColorMetadataPass& pass) {
Require(pass.source.has_value() && pass.targets.size() == 1, "CB resolve without its source and destination");
const auto& source = *pass.source;
const auto& destination = pass.targets.front();
const auto what = "CB resolve " + hexAddress(source.address) + " -> " + hexAddress(destination.address);
const auto from = metadataPassResident(context, source);
const auto to = metadataPassResident(context, destination);
Require(from != nullptr && to != nullptr, what + " needs resident images of both targets (tiled targets with the GPU detiler)");
if (materializeCmaskClear(context, source, from)) from->MarkDirty();
const char* refusal = nullptr;
if (!to->CopyFrom(*from, refusal, true)) Require(false, what + " as an image copy was refused (" + (refusal != nullptr ? refusal : "unknown") + ")");
}

}

void RunColorMetadataPass(const Context& context, const ColorMetadataPass& pass) {
if (pass.mode == ColorMetadataPass::Mode::Resolve) {
resolveColorTarget(context, pass);
return;
}
for (const auto& color : pass.targets) {
if (color.cmaskAddress != 0) {
const auto resident = metadataPassResident(context, color);
Expand Down
Loading
Loading