Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions cmake/KazarovPort.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ function(kz_port_host_library target)
# The game's replacement units include "gfx/gfx.hpp", so the include root is part of this library's interface.
target_link_libraries(${target} PUBLIC kz_port_headers)
target_compile_options(${target} PRIVATE -Wall -ffunction-sections -fdata-sections)
# arm64 clang fuses a*b+c into one rounding and x86-64 has no FMA in its baseline. The logical-to-pixel
# mapping floors such sums, so a fused one lands a whole pixel off on arm64 alone.
target_compile_options(${target} PRIVATE -ffp-contract=off)
endfunction()

function(kz_port_sce_sources out_var)
Expand Down
2 changes: 1 addition & 1 deletion cmake/KazarovRust.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ if(WIN32)
target_link_libraries(kazarov_core INTERFACE imm32 uxtheme opengl32 dwmapi)
elseif(APPLE)
target_link_libraries(kazarov_core INTERFACE
"-framework AppKit" "-framework CoreFoundation" "-framework CoreGraphics"
"-framework AppKit" "-framework Carbon" "-framework CoreFoundation" "-framework CoreGraphics"
"-framework CoreVideo" "-framework Foundation" "-framework IOKit"
"-framework OpenGL" "-framework QuartzCore" "-framework UniformTypeIdentifiers" objc)
endif()
1 change: 1 addition & 0 deletions include/audio/track_cache.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class TrackCache {
TrackCache &operator=(const TrackCache &) = delete;

// At most sixteen pending requests and one decode. A full queue returns null.
// The last 256 failed keys are remembered separately until Reset, without evicting PCM.
std::shared_ptr<const PendingTrack> Request(const std::string &key);
// Invalidates queued/in-flight work without joining the worker. Existing playback owns its PCM.
void Reset(Loader loader);
Expand Down
14 changes: 0 additions & 14 deletions include/gfx/displaylist.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ struct Draw2DEntry {
TextureHandle target = kMainTarget;
std::vector<Vertex2D> presentation_vertices;
DrawState presentation_state;
int32_t composite = -1;
};

// The projection and camera the meshes around a 3D sprite were drawn with, and where the target's
Expand Down Expand Up @@ -73,7 +72,6 @@ struct ClearEntry {
LogicalRect rect = {};
bool has_presentation_rect = false;
LogicalRect presentation_rect = {};
int32_t composite = -1;
};

struct TargetEntry {
Expand Down Expand Up @@ -125,16 +123,6 @@ struct MeshRecord {
Mat4 local = {};
};

struct CompositeRegion {
InterpKey key;
uint32_t occurrence;
LogicalRect bounds;
LogicalRect visible;
float padding;
};

LogicalRect CompositeBounds(const CompositeRegion &now, const CompositeRegion *before);

// What a display render of a list against one predecessor reuses from frame to frame.
struct MatchCache {
std::vector<std::array<uint32_t, 2> > draws_2d; // matched entry indices, newest then previous
Expand Down Expand Up @@ -170,8 +158,6 @@ struct DisplayList {
std::unordered_map<InterpKey, uint32_t> occurrences_2d;
std::vector<uint32_t> tagged_2d;
mutable detail::MatchCache cache;
std::vector<detail::CompositeRegion> composites;
int32_t composite = -1;

DisplayList() = default;
DisplayList(const DisplayList &) = delete;
Expand Down
7 changes: 0 additions & 7 deletions include/gfx/gfx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -656,13 +656,6 @@ using DisplayListRef = std::shared_ptr<const DisplayList>;
// points of the same space from tick to tick (the cloth's), not for geometry rebuilt in a space or
// an order of its own every tick (the shadow volumes).
using InterpKey = uint64_t;

// Group a scratch-image clear and its screen-space copies. Display rendering bounds them to
// the union of this part's two tick rectangles, padded in logical pixels. Canonical draws are
// unchanged. Keys identify parts; repeated uses of a key pair in submission order.
void BeginComposite(InterpKey key, const LogicalRect &bounds, const LogicalRect &visible, float padding);
void EndComposite();

void SetInterpKey(InterpKey key, bool no_interpolation = false, float teleport_distance = INFINITY,
bool blend_vertices = false);
InterpKey CurrentInterpKey();
Expand Down
18 changes: 16 additions & 2 deletions src/cpp/audio/track_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ struct TrackCache::State {
Loader loader;
std::deque<Job> queue;
std::list<Job> cached;
std::deque<Job> failed;
Job active;
uint64_t generation = 0;
size_t bytes = 0, max_entries, max_bytes;
Expand Down Expand Up @@ -59,7 +60,13 @@ struct TrackCache::State {
}
const size_t size = track ? track->samples.size() * sizeof(int16_t) : 0;
// Oversized tracks may be played, but never retained in the cache.
if (size <= max_bytes && max_entries > 0) {
if (!track) {
// Negative results have a separate bounded budget, cleared on reload.
if (failed.size() == 256) {
failed.pop_front();
}
failed.push_back(job);
} else if (size <= max_bytes && max_entries > 0) {
while (!cached.empty() && (cached.size() >= max_entries || bytes + size > max_bytes)) {
const auto &old = cached.back().result->track;
bytes -= old ? old->samples.size() * sizeof(int16_t) : 0;
Expand All @@ -69,9 +76,10 @@ struct TrackCache::State {
cached.push_front(job);
}
job.result->track = std::move(track);
job.result->ready.store(true, std::memory_order_release);
}
// Large PCM allocations are freed on the worker, outside the queue lock.
evicted.clear();
job.result->ready.store(true, std::memory_order_release);
}
}
};
Expand All @@ -97,6 +105,11 @@ std::shared_ptr<const PendingTrack> TrackCache::Request(const std::string &key)
return result;
}
}
for (const auto &job : state_->failed) {
if (job.key == key) {
return job.result;
}
}
if (state_->active.result && state_->active.generation == state_->generation && state_->active.key == key) {
return state_->active.result;
}
Expand All @@ -122,6 +135,7 @@ void TrackCache::Reset(Loader loader) {
job.result->ready.store(true, std::memory_order_release);
}
state_->queue.clear();
state_->failed.clear();
evicted.splice(evicted.end(), state_->cached);
state_->bytes = 0;
state_->loader = std::move(loader);
Expand Down
44 changes: 35 additions & 9 deletions src/cpp/gfx/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ renders one.
loading screen took the window). `options.overlay` names a second list whose drawing entries run
after the first's, uninterpolated, on `kMainTarget`, before the present: the host's FPS counter,
which so never reaches a canonical image.
- **Composite bounds.** A recorded `Draw2D` can supply `Presentation2D` replacement vertices and state for a
- **Presentation composites.** A recorded `Draw2D` can supply `Presentation2D` replacement vertices and state for a
screen-space composite of a model image that is redrawn during presentation. The
replacement is copied at recording time and is used only by display replay; the
canonical and immediate draw retain the original vertices and state. The
Expand All @@ -345,14 +345,20 @@ renders one.
the canonical object's bounds cannot leak into the enlarged composite. Callers
must preserve texture-to-screen mapping and reject transparent fragments when an
enlarged composite writes depth. This is separate from keyed 2D interpolation.
`BeginComposite(key, bounds, visible, padding)` / `EndComposite()` group a part's
scratch clear and copies. Presentation copy scissors cover the union of its
two tick rectangles, expanded by the outline/filter padding and clipped to the visible
extent. Clears retain the caller's presentation rectangle: filtered, shifted copies can
sample beyond their scissor and must not pick up another part's stale scratch texels.
Copy scissors retain each copy's texture-to-screen offset. Full-size vertices
retain identical sampling; pixels outside the scissor incur no fragment work. Bounds
must enclose the part at each tick; keys must remain stable across adjacent ticks.
Outline callers provide full-visible-extent presentation vertices and scratch clears
so models remain complete under interpolated rotation, skinning and display-only
camera motion. Shifted, filtered copies can sample beyond their scissors, so scratch
clears retain the full caller rectangle instead of a copy scissor. Each copy retains
its texture-to-screen offset. Canonical clear/copy
rectangles stay unchanged; transparent texels in depth-writing replacements never
write depth. Callers that copy recorded display lists must preserve
`presentation_vertices` and presentation clear rectangles, and leave resource
retirement ownership with the source list. The shared GPU regressions
`GfxGpuMeshDc2.RotatingThinOutlineKeepsIntermediateTips` and
`InterpolatedOutlineEscapesNewestPartCrop` cover interpolated outline extent;
`GfxGpuDisplayList.PresentationCompositeKeepsMovingMeshAndClearsReusedTarget`
and `PresentationCompositeDoesNotWriteTransparentDepth` cover replacement
composites, scratch clears and transparent depth rejection.
- **Interpolation.** A mesh draw may carry a `MeshTransform` (projection, view, middle, model,
local: `mvp` is their product) and is tagged with the current `InterpKey` (`SetInterpKey`). A
display render at `alpha` < 1 with `options.previous` matches each tagged draw with the previous
Expand Down Expand Up @@ -479,3 +485,23 @@ what 1.3 supports (`spirv-val --target-env vulkan1.3` accepts all three).
marking the pixels whose alpha passes, then a test against it, is the way.
- GS mip LOD (TEX1 L and K) is ignored; filtering is standard trilinear over the supplied levels.
- The device requirements are listed under "Device".

Surface resynchronization requires matching material/range, handedness and part geometry
before consuming a predecessor. Buffered meshes keep their stable mesh handle. Immediate
parts require matching index topology, vertex count and immutable vertex bytes. An explicit
vertex-blending opt-in allows position/normal changes only within the same occurrence with
unchanged UV/color attributes; moving immediate parts without a stable mesh identity do not
resynchronize across occurrences. This preserves the caller's same-points/order contract.

## Shared device test classification

Shared recording, pairing, composite, Prog0, lifetime and frame-edge device tests use
suite names beginning with `GfxGpu`, including parameterized suites. Consumers can
exclude them with the single GoogleTest filter `-*GfxGpu*.*`, or CTest's
`-E "(^|/)GfxGpu"`. Register that same family with the `gpu` label and a device-suitable
timeout (30 seconds) when using label-based CI exclusion. Pure CPU display-list
math remains in `GfxDisplayList`; `GfxMeshDc2.MeshConstantsKeepChroniclesSize` is
also CPU-only. Existing renderer/texture GPU families retain their existing exclusions.
A consumer that previously excluded only `^(GfxMisc|TexDecode)\.` must add
`|(^|/)GfxGpu` to its CTest exclusion before adopting this dependency, or label
this family and use `-LE gpu`. CPU display-list coverage continues to run.
Loading
Loading