[Phi] adding full formats support, finishing blitter port from software
Mirror Gitea refs to GitHub / mirror (push) Successful in 13s
Test / build_and_test (push) Successful in 5m42s
Build / build (push) Successful in 7m19s

blitter
This commit is contained in:
2026-08-21 22:38:31 +02:00
parent cbbe113b9b
commit 2525dded67
10 changed files with 2206 additions and 236 deletions
+2 -1
View File
@@ -607,6 +607,7 @@ fn addPhiDaemon(b: *std.Build, optimize: std.builtin.OptimizeMode, cc: []const u
const sources = [_][]const u8{ const sources = [_][]const u8{
"src/phi/mic/main.c", "src/phi/mic/main.c",
"src/phi/mic/Blitter.c", "src/phi/mic/Blitter.c",
"src/phi/mic/BlitFormats.c",
"src/phi/mic/Buffer.c", "src/phi/mic/Buffer.c",
"src/phi/mic/CommandBuffer.c", "src/phi/mic/CommandBuffer.c",
"src/phi/mic/Daemon.c", "src/phi/mic/Daemon.c",
@@ -648,7 +649,7 @@ fn addPhiDaemon(b: *std.Build, optimize: std.builtin.OptimizeMode, cc: []const u
cmd.addFileArg(avx_object); cmd.addFileArg(avx_object);
} }
cmd.addArgs(&.{ "-lscif", "-o" }); cmd.addArgs(&.{ "-lscif", "-lm", "-o" });
return cmd.addOutputFileArg("phi_device.mic"); return cmd.addOutputFileArg("phi_device.mic");
} }
+109 -10
View File
@@ -18,6 +18,15 @@ pub const Interface = base.Queue;
const ring_capacity: usize = @intCast(proto.PHI_QUEUE_RING_CAPACITY); const ring_capacity: usize = @intCast(proto.PHI_QUEUE_RING_CAPACITY);
const ring_capacity_u64: u64 = @intCast(ring_capacity); const ring_capacity_u64: u64 = @intCast(ring_capacity);
const shutdown_sequence = std.math.maxInt(u64); const shutdown_sequence = std.math.maxInt(u64);
const shutdown_timeout_ns = 5 * std.time.ns_per_s;
const shutdown_poll_ns = 10 * std.time.ns_per_ms;
const CompletionShutdown = enum {
acknowledged,
stopped_without_acknowledgement,
timed_out,
wait_failed,
};
const PreparedSubmit = struct { const PreparedSubmit = struct {
wait_semaphores: std.ArrayList(*base.BinarySemaphore), wait_semaphores: std.ArrayList(*base.BinarySemaphore),
@@ -63,6 +72,7 @@ pending: [ring_capacity]?PendingCompletion,
error_state: ?VkError, error_state: ?VkError,
shutting_down: bool, shutting_down: bool,
completion_stopped: bool,
remote_stopped: bool, remote_stopped: bool,
pub fn create(allocator: std.mem.Allocator, device: *base.Device, index: u32, family_index: u32, flags: vk.DeviceQueueCreateFlags) VkError!*Interface { pub fn create(allocator: std.mem.Allocator, device: *base.Device, index: u32, family_index: u32, flags: vk.DeviceQueueCreateFlags) VkError!*Interface {
@@ -129,6 +139,7 @@ pub fn create(allocator: std.mem.Allocator, device: *base.Device, index: u32, fa
.pending = [_]?PendingCompletion{null} ** ring_capacity, .pending = [_]?PendingCompletion{null} ** ring_capacity,
.error_state = null, .error_state = null,
.shutting_down = false, .shutting_down = false,
.completion_stopped = false,
.remote_stopped = false, .remote_stopped = false,
}; };
@@ -141,27 +152,63 @@ pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) VkError!void
const io = interface.owner.io(); const io = interface.owner.io();
const device_allocator = interface.owner.device_allocator.allocator(); const device_allocator = interface.owner.device_allocator.allocator();
var graceful_shutdown = true;
waitIdle(interface) catch |err| { waitIdle(interface) catch |err| {
graceful_shutdown = false;
std.log.scoped(.PhiQueue).warn("Queue did not become idle during destruction: {s}", .{@errorName(err)}); std.log.scoped(.PhiQueue).warn("Queue did not become idle during destruction: {s}", .{@errorName(err)});
}; };
var mutex_locked = true;
var graceful_shutdown = true;
self.mutex.lock(io) catch { self.mutex.lock(io) catch {
mutex_locked = false;
graceful_shutdown = false; graceful_shutdown = false;
}; };
if (graceful_shutdown) { var shutdown_next_sequence: u64 = 0;
var shutdown_completed_sequence: u64 = 0;
if (mutex_locked) {
self.shutting_down = true; self.shutting_down = true;
shutdown_next_sequence = self.next_remote_sequence;
shutdown_completed_sequence = self.completed_sequence;
self.condition.broadcast(io); self.condition.broadcast(io);
self.mutex.unlock(io); self.mutex.unlock(io);
}
if (graceful_shutdown) {
std.log.scoped(.PhiQueue).info(
"Sending shutdown doorbell (next remote sequence {d}, completed {d})",
.{ shutdown_next_sequence, shutdown_completed_sequence },
);
self.transport.sendQueueDoorbell(shutdown_sequence) catch |err| { self.transport.sendQueueDoorbell(shutdown_sequence) catch |err| {
graceful_shutdown = false; graceful_shutdown = false;
std.log.scoped(.PhiQueue).warn("Failed to send queue shutdown doorbell: {s}", .{@errorName(err)}); std.log.scoped(.PhiQueue).warn("Failed to send queue shutdown doorbell: {s}", .{@errorName(err)});
self.transport.close();
}; };
} else {
// Wake the blocking completion receiver before releasing queue storage if (graceful_shutdown) {
self.transport.close(); switch (self.waitForCompletionShutdown(io)) {
.acknowledged => {},
.stopped_without_acknowledgement => {
graceful_shutdown = false;
std.log.scoped(.PhiQueue).warn("Remote queue stopped without acknowledging shutdown", .{});
},
.timed_out => {
graceful_shutdown = false;
const progress = self.completionProgress(io);
std.log.scoped(.PhiQueue).warn(
"Timed out waiting for remote queue shutdown (next remote sequence {d}, completed {d}); closing SCIF endpoint",
.{ progress.next, progress.completed },
);
},
.wait_failed => {
graceful_shutdown = false;
std.log.scoped(.PhiQueue).warn("Failed while waiting for remote queue shutdown; closing SCIF endpoint", .{});
},
}
}
}
if (!graceful_shutdown) {
// Wake the blocking completion receiver before releasing queue storage.
// Keep libscif loaded until the receiver has returned from scif_recv.
self.transport.interrupt();
} }
self.completion_group.await(io) catch |err| { self.completion_group.await(io) catch |err| {
@@ -521,9 +568,16 @@ fn publish(self: *Self, prepared: *PreparedSubmit, fence: ?*base.Fence) VkError!
} }
fn completionRunner(self: *Self) void { fn completionRunner(self: *Self) void {
defer self.markCompletionStopped();
while (true) { while (true) {
const completion = self.transport.receiveQueueCompletion() catch { const completion = self.transport.receiveQueueCompletion() catch |err| {
if (!self.isShuttingDown()) self.markLost(VkError.DeviceLost); if (!self.isShuttingDown()) {
std.log.scoped(.PhiQueue).err("Queue completion receive failed: {s}", .{@errorName(err)});
self.markLost(VkError.DeviceLost);
} else {
std.log.scoped(.PhiQueue).warn("Queue completion receiver stopped without shutdown acknowledgement: {s}", .{@errorName(err)});
}
return; return;
}; };
@@ -531,6 +585,7 @@ fn completionRunner(self: *Self) void {
const io = self.interface.owner.io(); const io = self.interface.owner.io();
self.mutex.lock(io) catch return; self.mutex.lock(io) catch return;
self.remote_stopped = completion.status == proto.PHI_STATUS_OK; self.remote_stopped = completion.status == proto.PHI_STATUS_OK;
std.log.scoped(.PhiQueue).info("Received remote queue shutdown acknowledgement (status {d})", .{completion.status});
self.condition.broadcast(io); self.condition.broadcast(io);
self.mutex.unlock(io); self.mutex.unlock(io);
return; return;
@@ -574,7 +629,14 @@ fn completeOne(self: *Self, completion: proto.PhiQueueCompletion) void {
if (pending.command_backing) |backing| allocator.free(backing); if (pending.command_backing) |backing| allocator.free(backing);
if (completion.status != proto.PHI_STATUS_OK or cleanup_failed) { if (completion.status != proto.PHI_STATUS_OK or cleanup_failed) {
self.markLost(VkError.DeviceLost); std.log.scoped(.PhiQueue).err(
"Queue completion {d} failed with remote status {d} (cleanup failed: {})",
.{ completion.sequence, completion.status, cleanup_failed },
);
// A remote command error is confined to this submission. The protocol
// stream and ring remain synchronized, so poisoning every later CTS
// submission would only hide the command that actually failed.
if (cleanup_failed) self.markLost(VkError.DeviceLost);
failPending(&pending); failPending(&pending);
} else if (self.hasError()) { } else if (self.hasError()) {
failPending(&pending); failPending(&pending);
@@ -649,6 +711,43 @@ fn hasError(self: *Self) bool {
return self.error_state != null; return self.error_state != null;
} }
fn completionProgress(self: *Self, io: std.Io) struct { next: u64, completed: u64 } {
self.mutex.lock(io) catch return .{ .next = 0, .completed = 0 };
defer self.mutex.unlock(io);
return .{ .next = self.next_remote_sequence, .completed = self.completed_sequence };
}
fn waitForCompletionShutdown(self: *Self, io: std.Io) CompletionShutdown {
const deadline = std.Io.Clock.Timestamp.fromNow(io, .{
.raw = .fromNanoseconds(shutdown_timeout_ns),
.clock = .awake,
});
while (true) {
self.mutex.lock(io) catch return .wait_failed;
const stopped = self.completion_stopped;
const acknowledged = self.remote_stopped;
self.mutex.unlock(io);
if (stopped) return if (acknowledged) .acknowledged else .stopped_without_acknowledgement;
const remaining = deadline.durationFromNow(io);
if (remaining.raw.nanoseconds <= 0) return .timed_out;
(std.Io.Clock.Duration{
.raw = .fromNanoseconds(@min(remaining.raw.nanoseconds, shutdown_poll_ns)),
.clock = .awake,
}).sleep(io) catch return .wait_failed;
}
}
fn markCompletionStopped(self: *Self) void {
const io = self.interface.owner.io();
self.mutex.lock(io) catch return;
self.completion_stopped = true;
self.condition.broadcast(io);
self.mutex.unlock(io);
}
fn isShuttingDown(self: *Self) bool { fn isShuttingDown(self: *Self) bool {
const io = self.interface.owner.io(); const io = self.interface.owner.io();
self.mutex.lock(io) catch return true; self.mutex.lock(io) catch return true;
+34 -10
View File
@@ -12,6 +12,8 @@ const Self = @This();
epd: Endpoint, epd: Endpoint,
sequence: u64 = 1, sequence: u64 = 1,
mutex: std.Io.Mutex = .init, mutex: std.Io.Mutex = .init,
endpoint_mutex: base.SpinMutex = .{},
library_loaded: bool = true,
instance: *base.Instance, instance: *base.Instance,
node_id: u16, node_id: u16,
@@ -68,13 +70,25 @@ pub fn deinit(self: *Self) void {
std.log.scoped(.PhiTransport).info("Closed connection", .{}); std.log.scoped(.PhiTransport).info("Closed connection", .{});
} }
/// Close a transport without issuing an RPC shutdown. Queue transports switch /// Close the endpoint so a thread blocked in SCIF receive wakes up. The SCIF
/// to a raw full-duplex doorbell protocol after setup and must use this path /// library stays loaded until `close`, because that thread may still be
pub fn close(self: *Self) void { /// returning through a dynamically loaded function.
if (self.epd < 0) return; pub fn interrupt(self: *Self) void {
self.endpoint_mutex.lock();
closeEndpoint(self.epd); const endpoint = self.epd;
self.epd = -1; self.epd = -1;
self.endpoint_mutex.unlock();
if (endpoint >= 0) closeEndpoint(endpoint);
}
/// Close a transport without issuing an RPC shutdown. Queue transports switch
/// to a raw full-duplex doorbell protocol after setup and must use this path.
pub fn close(self: *Self) void {
self.interrupt();
if (!self.library_loaded) return;
self.library_loaded = false;
scif.unload(); scif.unload();
} }
@@ -137,9 +151,10 @@ pub fn statusToErr(status: c_int) VkError {
} }
fn writeAll(self: *Self, bytes: []const u8) VkError!void { fn writeAll(self: *Self, bytes: []const u8) VkError!void {
const endpoint = self.getEndpoint() orelse return VkError.DeviceLost;
var offset: usize = 0; var offset: usize = 0;
while (offset < bytes.len) { while (offset < bytes.len) {
const written = scif.send(self.epd, bytes[offset..].ptr, bytes.len - offset, scif.send_block); const written = scif.send(endpoint, bytes[offset..].ptr, bytes.len - offset, scif.send_block);
if (written <= 0) { if (written <= 0) {
return VkError.DeviceLost; return VkError.DeviceLost;
} }
@@ -148,9 +163,10 @@ fn writeAll(self: *Self, bytes: []const u8) VkError!void {
} }
fn readAll(self: *Self, bytes: []u8) VkError!void { fn readAll(self: *Self, bytes: []u8) VkError!void {
const endpoint = self.getEndpoint() orelse return VkError.DeviceLost;
var offset: usize = 0; var offset: usize = 0;
while (offset < bytes.len) { while (offset < bytes.len) {
const read = scif.recv(self.epd, bytes[offset..].ptr, bytes.len - offset, scif.recv_block); const read = scif.recv(endpoint, bytes[offset..].ptr, bytes.len - offset, scif.recv_block);
if (read <= 0) { if (read <= 0) {
return VkError.DeviceLost; return VkError.DeviceLost;
} }
@@ -158,6 +174,12 @@ fn readAll(self: *Self, bytes: []u8) VkError!void {
} }
} }
fn getEndpoint(self: *Self) ?Endpoint {
self.endpoint_mutex.lock();
defer self.endpoint_mutex.unlock();
return if (self.epd >= 0) self.epd else null;
}
fn closeEndpoint(endpoint: Endpoint) void { fn closeEndpoint(endpoint: Endpoint) void {
_ = scif.close(endpoint); _ = scif.close(endpoint);
} }
@@ -181,8 +203,9 @@ fn handshake(self: *Self) VkError!void {
} }
pub fn registerHostMemory(self: *Self, memory: []u8) VkError!u64 { pub fn registerHostMemory(self: *Self, memory: []u8) VkError!u64 {
const endpoint = self.getEndpoint() orelse return VkError.DeviceLost;
const offset = scif.register( const offset = scif.register(
self.epd, endpoint,
memory.ptr, memory.ptr,
memory.len, memory.len,
0, 0,
@@ -196,7 +219,8 @@ pub fn registerHostMemory(self: *Self, memory: []u8) VkError!u64 {
} }
pub fn unregisterHostMemory(self: *Self, offset: u64, size: usize) VkError!void { pub fn unregisterHostMemory(self: *Self, offset: u64, size: usize) VkError!void {
if (scif.unregister(self.epd, @intCast(offset), size) != 0) { const endpoint = self.getEndpoint() orelse return VkError.DeviceLost;
if (scif.unregister(endpoint, @intCast(offset), size) != 0) {
return VkError.Unknown; return VkError.Unknown;
} }
} }
File diff suppressed because it is too large Load Diff
+43
View File
@@ -0,0 +1,43 @@
#ifndef APE_PHI_BLIT_FORMATS_H
#define APE_PHI_BLIT_FORMATS_H
#include <Protocol.h>
#include <stdint.h>
typedef struct PhiBlitFormatInfo
{
uint32_t texel_size;
uint8_t is_integer;
uint8_t is_signed;
uint8_t is_float;
uint8_t is_srgb;
uint8_t is_unsigned;
uint8_t can_read_float;
uint8_t can_write_float;
uint8_t can_read_int;
uint8_t can_write_int;
uint8_t vector_unorm8x4;
} PhiBlitFormatInfo;
typedef struct PhiBlitFloat4
{
float values[4];
} PhiBlitFloat4;
typedef struct PhiBlitInt4
{
uint32_t values[4];
} PhiBlitInt4;
int PhiGetBlitFormatInfo(PhiFormat format, PhiBlitFormatInfo* info);
PhiBlitFloat4 PhiReadBlitFloat4(const uint8_t* map, PhiFormat format);
void PhiWriteBlitFloat4(PhiBlitFloat4 color, uint8_t* map, PhiFormat format);
PhiBlitInt4 PhiReadBlitInt4(const uint8_t* map, PhiFormat format);
void PhiWriteBlitInt4(PhiBlitInt4 color, uint8_t* map, PhiFormat format);
PhiBlitFloat4 PhiConvertBlitFloat4(PhiBlitFloat4 color,
PhiFormat src_format,
PhiFormat dst_format,
int allow_srgb_conversion,
int apply_srgb_conversion);
#endif
+146 -189
View File
@@ -1,3 +1,4 @@
#include <BlitFormats.h>
#include <Blitter.h> #include <Blitter.h>
#include <Logger.h> #include <Logger.h>
#include <Memory.h> #include <Memory.h>
@@ -8,7 +9,6 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#define UNORM8_SCALE (1.0f / 255.0f)
#define BLIT_WEIGHT_SCALE 1024.0f #define BLIT_WEIGHT_SCALE 1024.0f
#define BLIT_PARALLEL_MIN_PIXELS (256u * 1024u) #define BLIT_PARALLEL_MIN_PIXELS (256u * 1024u)
#define BLIT_TASK_TARGET_BYTES (64u * 1024u) #define BLIT_TASK_TARGET_BYTES (64u * 1024u)
@@ -20,18 +20,8 @@ enum
PHI_FILTER_LINEAR = 1, PHI_FILTER_LINEAR = 1,
}; };
typedef struct Color typedef PhiBlitFloat4 Color;
{ typedef PhiBlitFormatInfo FormatInfo;
float r;
float g;
float b;
float a;
} Color;
typedef struct FormatInfo
{
uint32_t texel_size;
} FormatInfo;
typedef struct SampleCoordinate typedef struct SampleCoordinate
{ {
@@ -51,149 +41,11 @@ typedef struct BlitWork
uint64_t rows_per_layer; uint64_t rows_per_layer;
} BlitWork; } BlitWork;
static int GetFormatInfo(PhiFormat format, FormatInfo* info)
{
switch(format)
{
case PHI_FORMAT_R8_UNORM:
info->texel_size = 1;
return 1;
case PHI_FORMAT_R8G8_UNORM:
info->texel_size = 2;
return 1;
case PHI_FORMAT_R8G8B8_UNORM:
case PHI_FORMAT_B8G8R8_UNORM:
info->texel_size = 3;
return 1;
case PHI_FORMAT_R8G8B8A8_UNORM:
case PHI_FORMAT_B8G8R8A8_UNORM:
case PHI_FORMAT_A8B8G8R8_UNORM_PACK32:
info->texel_size = 4;
return 1;
default:
return 0;
}
}
static Color ReadUnorm8(const uint8_t* texel, PhiFormat format)
{
Color color = { 0.0f, 0.0f, 0.0f, 1.0f };
switch(format)
{
case PHI_FORMAT_R8_UNORM:
color.r = (float)texel[0] * UNORM8_SCALE;
break;
case PHI_FORMAT_R8G8_UNORM:
color.r = (float)texel[0] * UNORM8_SCALE;
color.g = (float)texel[1] * UNORM8_SCALE;
break;
case PHI_FORMAT_R8G8B8_UNORM:
color.r = (float)texel[0] * UNORM8_SCALE;
color.g = (float)texel[1] * UNORM8_SCALE;
color.b = (float)texel[2] * UNORM8_SCALE;
break;
case PHI_FORMAT_B8G8R8_UNORM:
color.r = (float)texel[2] * UNORM8_SCALE;
color.g = (float)texel[1] * UNORM8_SCALE;
color.b = (float)texel[0] * UNORM8_SCALE;
break;
case PHI_FORMAT_R8G8B8A8_UNORM:
case PHI_FORMAT_A8B8G8R8_UNORM_PACK32:
color.r = (float)texel[0] * UNORM8_SCALE;
color.g = (float)texel[1] * UNORM8_SCALE;
color.b = (float)texel[2] * UNORM8_SCALE;
color.a = (float)texel[3] * UNORM8_SCALE;
break;
case PHI_FORMAT_B8G8R8A8_UNORM:
color.r = (float)texel[2] * UNORM8_SCALE;
color.g = (float)texel[1] * UNORM8_SCALE;
color.b = (float)texel[0] * UNORM8_SCALE;
color.a = (float)texel[3] * UNORM8_SCALE;
break;
default:
break;
}
return color;
}
static inline uint8_t QuantizeUnorm8(float value)
{
if(value <= 0.0f)
return 0;
if(value >= 1.0f)
return UINT8_MAX;
return (uint8_t)(value * 255.0f + 0.5f);
}
static void WriteUnorm8(uint8_t* texel, PhiFormat format, Color color)
{
const uint8_t r = QuantizeUnorm8(color.r);
const uint8_t g = QuantizeUnorm8(color.g);
const uint8_t b = QuantizeUnorm8(color.b);
const uint8_t a = QuantizeUnorm8(color.a);
switch(format)
{
case PHI_FORMAT_R8_UNORM:
texel[0] = r;
break;
case PHI_FORMAT_R8G8_UNORM:
texel[0] = r;
texel[1] = g;
break;
case PHI_FORMAT_R8G8B8_UNORM:
texel[0] = r;
texel[1] = g;
texel[2] = b;
break;
case PHI_FORMAT_B8G8R8_UNORM:
texel[0] = b;
texel[1] = g;
texel[2] = r;
break;
case PHI_FORMAT_R8G8B8A8_UNORM:
case PHI_FORMAT_A8B8G8R8_UNORM_PACK32:
texel[0] = r;
texel[1] = g;
texel[2] = b;
texel[3] = a;
break;
case PHI_FORMAT_B8G8R8A8_UNORM:
texel[0] = b;
texel[1] = g;
texel[2] = r;
texel[3] = a;
break;
default:
break;
}
}
static inline Color LerpColor(Color a, Color b, float factor) static inline Color LerpColor(Color a, Color b, float factor)
{ {
Color result; Color result;
result.r = a.r + (b.r - a.r) * factor; for(uint32_t component = 0; component < 4; ++component)
result.g = a.g + (b.g - a.g) * factor; result.values[component] = a.values[component] + (b.values[component] - a.values[component]) * factor;
result.b = a.b + (b.b - a.b) * factor;
result.a = a.a + (b.a - a.a) * factor;
return result; return result;
} }
@@ -238,59 +90,133 @@ static Color ReadTexel(const uint8_t* src,
uint32_t z) uint32_t z)
{ {
const uint64_t offset = (uint64_t)z * slice_pitch + (uint64_t)y * row_pitch + (uint64_t)x * texel_size; const uint64_t offset = (uint64_t)z * slice_pitch + (uint64_t)y * row_pitch + (uint64_t)x * texel_size;
return ReadUnorm8(src + (size_t)offset, format); return PhiReadBlitFloat4(src + (size_t)offset, format);
}
static inline Color PrepareLinearSample(Color color,
const PhiCmdBlitImage* command,
const FormatInfo* src_info,
int* apply_srgb_conversion)
{
if(command->allow_srgb_conversion && src_info->is_srgb)
{
*apply_srgb_conversion = 0;
return PhiConvertBlitFloat4(color, (PhiFormat)command->src_format, (PhiFormat)command->dst_format, 1, 1);
}
return color;
} }
static Color static Color
Sample(const uint8_t* src, const PhiCmdBlitImage* command, uint32_t texel_size, float x, float y, float z, int filter_3d) Sample(const uint8_t* src, const PhiCmdBlitImage* command, const FormatInfo* src_info, float x, float y, float z, int filter_3d)
{ {
const PhiFormat format = (PhiFormat)command->src_format; const PhiFormat format = (PhiFormat)command->src_format;
int apply_srgb_conversion = 1;
if(command->filter == PHI_FILTER_NEAREST) if(command->filter == PHI_FILTER_NEAREST)
{ {
return ReadTexel(src, Color color = ReadTexel(src,
format, format,
texel_size, src_info->texel_size,
command->src_row_pitch, command->src_row_pitch,
command->src_slice_pitch, command->src_slice_pitch,
GetNearestCoordinate(x, command->src_width), GetNearestCoordinate(x, command->src_width),
GetNearestCoordinate(y, command->src_height), GetNearestCoordinate(y, command->src_height),
GetNearestCoordinate(z, command->src_depth)); GetNearestCoordinate(z, command->src_depth));
return PhiConvertBlitFloat4(color, format, (PhiFormat)command->dst_format, command->allow_srgb_conversion, 1);
} }
const SampleCoordinate sample_x = GetLinearCoordinate(x, command->src_width); const SampleCoordinate sample_x = GetLinearCoordinate(x, command->src_width);
const SampleCoordinate sample_y = GetLinearCoordinate(y, command->src_height); const SampleCoordinate sample_y = GetLinearCoordinate(y, command->src_height);
const SampleCoordinate sample_z = GetLinearCoordinate(z, command->src_depth); const SampleCoordinate sample_z = GetLinearCoordinate(z, command->src_depth);
const Color color_0_0 = ReadTexel( Color color_0_0 = ReadTexel(src,
src, format, texel_size, command->src_row_pitch, command->src_slice_pitch, sample_x.lo, sample_y.lo, sample_z.lo); format,
const Color color_0_1 = ReadTexel( src_info->texel_size,
src, format, texel_size, command->src_row_pitch, command->src_slice_pitch, sample_x.hi, sample_y.lo, sample_z.lo); command->src_row_pitch,
const Color color_1_0 = ReadTexel( command->src_slice_pitch,
src, format, texel_size, command->src_row_pitch, command->src_slice_pitch, sample_x.lo, sample_y.hi, sample_z.lo); sample_x.lo,
const Color color_1_1 = ReadTexel( sample_y.lo,
src, format, texel_size, command->src_row_pitch, command->src_slice_pitch, sample_x.hi, sample_y.hi, sample_z.lo); sample_z.lo);
Color color_0_1 = ReadTexel(src,
format,
src_info->texel_size,
command->src_row_pitch,
command->src_slice_pitch,
sample_x.hi,
sample_y.lo,
sample_z.lo);
Color color_1_0 = ReadTexel(src,
format,
src_info->texel_size,
command->src_row_pitch,
command->src_slice_pitch,
sample_x.lo,
sample_y.hi,
sample_z.lo);
Color color_1_1 = ReadTexel(src,
format,
src_info->texel_size,
command->src_row_pitch,
command->src_slice_pitch,
sample_x.hi,
sample_y.hi,
sample_z.lo);
color_0_0 = PrepareLinearSample(color_0_0, command, src_info, &apply_srgb_conversion);
color_0_1 = PrepareLinearSample(color_0_1, command, src_info, &apply_srgb_conversion);
color_1_0 = PrepareLinearSample(color_1_0, command, src_info, &apply_srgb_conversion);
color_1_1 = PrepareLinearSample(color_1_1, command, src_info, &apply_srgb_conversion);
const Color row_0 = LerpColor(color_0_0, color_0_1, sample_x.factor); const Color row_0 = LerpColor(color_0_0, color_0_1, sample_x.factor);
const Color row_1 = LerpColor(color_1_0, color_1_1, sample_x.factor); const Color row_1 = LerpColor(color_1_0, color_1_1, sample_x.factor);
const Color slice_0 = LerpColor(row_0, row_1, sample_y.factor); const Color slice_0 = LerpColor(row_0, row_1, sample_y.factor);
if(!filter_3d) if(!filter_3d)
return slice_0; return PhiConvertBlitFloat4(
slice_0, format, (PhiFormat)command->dst_format, command->allow_srgb_conversion, apply_srgb_conversion);
const Color color_0_0_1 = ReadTexel( Color color_0_0_1 = ReadTexel(src,
src, format, texel_size, command->src_row_pitch, command->src_slice_pitch, sample_x.lo, sample_y.lo, sample_z.hi); format,
const Color color_0_1_1 = ReadTexel( src_info->texel_size,
src, format, texel_size, command->src_row_pitch, command->src_slice_pitch, sample_x.hi, sample_y.lo, sample_z.hi); command->src_row_pitch,
const Color color_1_0_1 = ReadTexel( command->src_slice_pitch,
src, format, texel_size, command->src_row_pitch, command->src_slice_pitch, sample_x.lo, sample_y.hi, sample_z.hi); sample_x.lo,
const Color color_1_1_1 = ReadTexel( sample_y.lo,
src, format, texel_size, command->src_row_pitch, command->src_slice_pitch, sample_x.hi, sample_y.hi, sample_z.hi); sample_z.hi);
Color color_0_1_1 = ReadTexel(src,
format,
src_info->texel_size,
command->src_row_pitch,
command->src_slice_pitch,
sample_x.hi,
sample_y.lo,
sample_z.hi);
Color color_1_0_1 = ReadTexel(src,
format,
src_info->texel_size,
command->src_row_pitch,
command->src_slice_pitch,
sample_x.lo,
sample_y.hi,
sample_z.hi);
Color color_1_1_1 = ReadTexel(src,
format,
src_info->texel_size,
command->src_row_pitch,
command->src_slice_pitch,
sample_x.hi,
sample_y.hi,
sample_z.hi);
color_0_0_1 = PrepareLinearSample(color_0_0_1, command, src_info, &apply_srgb_conversion);
color_0_1_1 = PrepareLinearSample(color_0_1_1, command, src_info, &apply_srgb_conversion);
color_1_0_1 = PrepareLinearSample(color_1_0_1, command, src_info, &apply_srgb_conversion);
color_1_1_1 = PrepareLinearSample(color_1_1_1, command, src_info, &apply_srgb_conversion);
const Color row_0_1 = LerpColor(color_0_0_1, color_0_1_1, sample_x.factor); const Color row_0_1 = LerpColor(color_0_0_1, color_0_1_1, sample_x.factor);
const Color row_1_1 = LerpColor(color_1_0_1, color_1_1_1, sample_x.factor); const Color row_1_1 = LerpColor(color_1_0_1, color_1_1_1, sample_x.factor);
const Color slice_1 = LerpColor(row_0_1, row_1_1, sample_y.factor); const Color slice_1 = LerpColor(row_0_1, row_1_1, sample_y.factor);
return LerpColor(slice_0, slice_1, sample_z.factor); Color color = LerpColor(slice_0, slice_1, sample_z.factor);
return PhiConvertBlitFloat4(
color, format, (PhiFormat)command->dst_format, command->allow_srgb_conversion, apply_srgb_conversion);
} }
static inline int IsMemoryRangeValid(const Memory* memory, uint64_t offset, uint64_t size) static inline int IsMemoryRangeValid(const Memory* memory, uint64_t offset, uint64_t size)
@@ -425,13 +351,26 @@ static inline void BlitScalarPixel(uint8_t* dst,
const uint8_t* src, const uint8_t* src,
const PhiCmdBlitImage* command, const PhiCmdBlitImage* command,
const FormatInfo* src_info, const FormatInfo* src_info,
const FormatInfo* dst_info,
float source_x, float source_x,
float source_y, float source_y,
float source_z, float source_z,
int filter_3d) int filter_3d)
{ {
const Color color = Sample(src, command, src_info->texel_size, source_x, source_y, source_z, filter_3d); if(src_info->is_integer && dst_info->is_integer)
WriteUnorm8(dst, (PhiFormat)command->dst_format, color); {
const uint32_t x = GetNearestCoordinate(source_x, command->src_width);
const uint32_t y = GetNearestCoordinate(source_y, command->src_height);
const uint32_t z = GetNearestCoordinate(source_z, command->src_depth);
const uint64_t offset =
(uint64_t)z * command->src_slice_pitch + (uint64_t)y * command->src_row_pitch + (uint64_t)x * src_info->texel_size;
const PhiBlitInt4 color = PhiReadBlitInt4(src + (size_t)offset, (PhiFormat)command->src_format);
PhiWriteBlitInt4(color, dst, (PhiFormat)command->dst_format);
return;
}
const Color color = Sample(src, command, src_info, source_x, source_y, source_z, filter_3d);
PhiWriteBlitFloat4(color, dst, (PhiFormat)command->dst_format);
} }
static void BlitRow(uint8_t* dst, static void BlitRow(uint8_t* dst,
@@ -448,7 +387,8 @@ static void BlitRow(uint8_t* dst,
// Fast path // Fast path
if(command->filter == PHI_FILTER_NEAREST && command->src_format == command->dst_format && command->step_x == 1.0f && if(command->filter == PHI_FILTER_NEAREST && command->src_format == command->dst_format && command->step_x == 1.0f &&
first_source_x >= 0.0f && first_source_x + (float)(pixel_count - 1) < (float)command->src_width) !(command->allow_srgb_conversion && (src_info->is_srgb || dst_info->is_srgb)) && first_source_x >= 0.0f &&
first_source_x + (float)(pixel_count - 1) < (float)command->src_width)
{ {
const uint32_t source_texel_x = GetNearestCoordinate(first_source_x, command->src_width); const uint32_t source_texel_x = GetNearestCoordinate(first_source_x, command->src_width);
const uint32_t source_texel_y = GetNearestCoordinate(source_y, command->src_height); const uint32_t source_texel_y = GetNearestCoordinate(source_y, command->src_height);
@@ -461,13 +401,14 @@ static void BlitRow(uint8_t* dst,
} }
uint32_t processed = 0; uint32_t processed = 0;
if(src_info->texel_size == 4 && dst_info->texel_size == 4 && command->src_width <= INT32_MAX) if(src_info->vector_unorm8x4 && dst_info->vector_unorm8x4 && command->src_width <= INT32_MAX)
{ {
while(processed < pixel_count && ((uintptr_t)(dst + (size_t)processed * 4) & 63) != 0) while(processed < pixel_count && ((uintptr_t)(dst + (size_t)processed * 4) & 63) != 0)
{ {
const int32_t destination_x = command->dst_x0 + (int32_t)processed; const int32_t destination_x = command->dst_x0 + (int32_t)processed;
const float source_x = command->src_x0 + (float)destination_x * command->step_x; const float source_x = command->src_x0 + (float)destination_x * command->step_x;
BlitScalarPixel(dst + (size_t)processed * 4, src, command, src_info, source_x, source_y, source_z, filter_3d); BlitScalarPixel(
dst + (size_t)processed * 4, src, command, src_info, dst_info, source_x, source_y, source_z, filter_3d);
++processed; ++processed;
} }
@@ -536,8 +477,15 @@ static void BlitRow(uint8_t* dst,
{ {
const int32_t destination_x = command->dst_x0 + (int32_t)processed; const int32_t destination_x = command->dst_x0 + (int32_t)processed;
const float source_x = command->src_x0 + (float)destination_x * command->step_x; const float source_x = command->src_x0 + (float)destination_x * command->step_x;
BlitScalarPixel( BlitScalarPixel(dst + (size_t)processed * dst_info->texel_size,
dst + (size_t)processed * dst_info->texel_size, src, command, src_info, source_x, source_y, source_z, filter_3d); src,
command,
src_info,
dst_info,
source_x,
source_y,
source_z,
filter_3d);
++processed; ++processed;
} }
} }
@@ -581,12 +529,21 @@ PhiStatus BlitImage(const PhiCmdBlitImage* command)
FormatInfo src_info; FormatInfo src_info;
FormatInfo dst_info; FormatInfo dst_info;
if(!GetFormatInfo((PhiFormat)command->src_format, &src_info) || !GetFormatInfo((PhiFormat)command->dst_format, &dst_info)) if(!PhiGetBlitFormatInfo((PhiFormat)command->src_format, &src_info) ||
!PhiGetBlitFormatInfo((PhiFormat)command->dst_format, &dst_info))
{ {
LogErrorFmt("Unsupported blit image formats: src=%u dst=%u", command->src_format, command->dst_format); LogErrorFmt("Unsupported blit image formats: src=%u dst=%u", command->src_format, command->dst_format);
return PHI_STATUS_INVALID_ARGUMENT; return PHI_STATUS_INVALID_ARGUMENT;
} }
const int integer_path = src_info.is_integer && dst_info.is_integer;
if((integer_path && (!src_info.can_read_int || !dst_info.can_write_int)) ||
(!integer_path && (!src_info.can_read_float || !dst_info.can_write_float)))
{
LogErrorFmt("Unsupported blit image format direction: src=%u dst=%u", command->src_format, command->dst_format);
return PHI_STATUS_INVALID_ARGUMENT;
}
PhiStatus status = ValidateCommand(command, src_memory, dst_memory, &src_info, &dst_info); PhiStatus status = ValidateCommand(command, src_memory, dst_memory, &src_info, &dst_info);
if(status != PHI_STATUS_OK) if(status != PHI_STATUS_OK)
return status; return status;
+1 -1
View File
@@ -7,7 +7,7 @@
#include <string.h> #include <string.h>
static const char* CommandName[] = { static const char* CommandName[] = {
"CopyBuffer", "FillBuffer", "CopyBufferToImage", "CopyImageToBuffer", "CopyImage", "CopyBuffer", "FillBuffer", "CopyBufferToImage", "CopyImageToBuffer", "CopyImage", "BlitImage",
}; };
PhiStatus ReadCommandData(PhiCommandReader* reader, void* data, uint64_t size) PhiStatus ReadCommandData(PhiCommandReader* reader, void* data, uint64_t size)
+29 -11
View File
@@ -82,7 +82,9 @@ int HandleNewMemory(PhiEndpoint endpoint, const PhiMessageHeader* header)
if(memory == NULL) if(memory == NULL)
LogErrorFmt("Failed to allocate %zu bytes", (size_t)request.size); LogErrorFmt("Failed to allocate %zu bytes", (size_t)request.size);
else else
LogInfoFmt("Allocated %llu bytes to handle 0x%X", request.size, (uintptr_t)memory); LogInfoFmt("Allocated %llu bytes to handle 0x%llX",
(unsigned long long)request.size,
(unsigned long long)(uintptr_t)memory);
} }
else if(header->type == PHI_PACKET_MAP_HOST_MEMORY) else if(header->type == PHI_PACKET_MAP_HOST_MEMORY)
{ {
@@ -93,7 +95,7 @@ int HandleNewMemory(PhiEndpoint endpoint, const PhiMessageHeader* header)
if(memory == NULL) if(memory == NULL)
reply.result.status = PHI_STATUS_MAP_HOST_MEMORY_FAILED; reply.result.status = PHI_STATUS_MAP_HOST_MEMORY_FAILED;
else else
LogInfoFmt("Mapped host memory to handle 0x%X", (uint64_t)(uintptr_t)memory); LogInfoFmt("Mapped host memory to handle 0x%llX", (unsigned long long)(uintptr_t)memory);
} }
if(memory != NULL) if(memory != NULL)
@@ -131,20 +133,36 @@ int HandleDestroyMemory(PhiEndpoint endpoint, const PhiMessageHeader* header)
if(request.remote_handle == 0) if(request.remote_handle == 0)
{ {
reply.result.status = PHI_STATUS_INVALID_HANDLE; reply.result.status = PHI_STATUS_INVALID_HANDLE;
LogErrorFmt("Could not free memory: invalid handle 0x%X", request.remote_handle); LogErrorFmt("Could not free memory: invalid handle 0x%llX", (unsigned long long)request.remote_handle);
} }
else else
{ {
const Memory* memory = (const Memory*)(uintptr_t)request.remote_handle; Memory* memory = (Memory*)(uintptr_t)request.remote_handle;
const MemoryType memory_type = memory->type;
const char* memory_type_name;
if(memory->type == PHI_MEMORY_LOCAL) if(memory_type == PHI_MEMORY_LOCAL)
free((void*)memory); memory_type_name = "local";
else if(memory->type == PHI_MEMORY_HOST_MAPPED) else if(memory_type == PHI_MEMORY_HOST_MAPPED)
scif_munmap((void*)memory->ptr, memory->size); memory_type_name = "host-mapped";
else
{
reply.result.status = PHI_STATUS_INVALID_HANDLE;
LogErrorFmt("Could not free memory handle 0x%llX: invalid memory type", (unsigned long long)request.remote_handle);
return SendReply(endpoint, header, &reply, sizeof(reply));
}
LogInfoFmt("Destroyed %s memory handle 0x%X", if(memory_type == PHI_MEMORY_HOST_MAPPED && scif_munmap(memory->ptr, (size_t)memory->scif_size) != 0)
memory->type == PHI_MEMORY_LOCAL ? "local" : "host-mapped", {
request.remote_handle); reply.result.status = PHI_STATUS_INVALID_HANDLE;
LogErrorFmt("Failed to unmap memory handle 0x%llX: %s", (unsigned long long)request.remote_handle, strerror(errno));
}
else
{
LogInfoFmt("Destroyed %s memory handle 0x%llX", memory_type_name, (unsigned long long)request.remote_handle);
}
free(memory);
} }
return SendReply(endpoint, header, &reply, sizeof(reply)); return SendReply(endpoint, header, &reply, sizeof(reply));
+25 -14
View File
@@ -49,16 +49,21 @@ static int SendQueueCompletion(PhiEndpoint endpoint, uint64_t sequence, PhiStatu
static int RunQueue(PhiEndpoint endpoint, volatile PhiQueueShared* shared) static int RunQueue(PhiEndpoint endpoint, volatile PhiQueueShared* shared)
{ {
uint64_t next_sequence = 1; uint64_t next_sequence = 1;
PhiStatus fatal_status = PHI_STATUS_OK;
for(;;) for(;;)
{ {
PhiQueueDoorbell doorbell; PhiQueueDoorbell doorbell;
if(ReadAll(endpoint, &doorbell, sizeof(doorbell)) < 0) if(ReadAll(endpoint, &doorbell, sizeof(doorbell)) < 0)
{
LogWarningFmt("Queue peer disconnected while waiting for sequence %llu", (unsigned long long)next_sequence);
return 0; return 0;
}
if(doorbell.sequence == PHI_QUEUE_SHUTDOWN_SEQUENCE) if(doorbell.sequence == PHI_QUEUE_SHUTDOWN_SEQUENCE)
{
LogInfoFmt("Received queue shutdown doorbell at sequence %llu", (unsigned long long)next_sequence);
return 1; return 1;
}
if(doorbell.sequence < next_sequence) if(doorbell.sequence < next_sequence)
continue; continue;
@@ -76,20 +81,14 @@ static int RunQueue(PhiEndpoint endpoint, volatile PhiQueueShared* shared)
.command_count = remote_submission->command_count, .command_count = remote_submission->command_count,
}; };
PhiStatus status = fatal_status; PhiStatus status;
if(status == PHI_STATUS_OK) if(submission.sequence != next_sequence)
{ status = PHI_STATUS_BAD_MESSAGE;
if(submission.sequence != next_sequence) else
status = PHI_STATUS_BAD_MESSAGE; status = ExecuteQueueSubmission(endpoint, &submission);
else
status = ExecuteQueueSubmission(endpoint, &submission);
}
if(status != PHI_STATUS_OK && fatal_status == PHI_STATUS_OK) if(status != PHI_STATUS_OK)
{
LogErrorFmt("Queue submission %llu failed: %s", (unsigned long long)next_sequence, StatusName[status]); LogErrorFmt("Queue submission %llu failed: %s", (unsigned long long)next_sequence, StatusName[status]);
fatal_status = status;
}
__atomic_store_n(&shared->completed_sequence, next_sequence, __ATOMIC_RELEASE); __atomic_store_n(&shared->completed_sequence, next_sequence, __ATOMIC_RELEASE);
if(SendQueueCompletion(endpoint, next_sequence, status) < 0) if(SendQueueCompletion(endpoint, next_sequence, status) < 0)
@@ -143,10 +142,22 @@ int HandleQueueSetup(PhiEndpoint endpoint, const PhiMessageHeader* header)
const int run_result = RunQueue(endpoint, shared); const int run_result = RunQueue(endpoint, shared);
if(scif_munmap((void*)shared, (size_t)request.scif_size) != 0) if(scif_munmap((void*)shared, (size_t)request.scif_size) != 0)
{
LogErrorFmt("Failed to unmap queue ring during shutdown: %s", strerror(errno));
return -1; return -1;
}
if(run_result == 1) if(run_result == 1)
return SendQueueCompletion(endpoint, PHI_QUEUE_SHUTDOWN_SEQUENCE, PHI_STATUS_OK); {
LogInfo("Queue ring unmapped; sending shutdown acknowledgement");
if(SendQueueCompletion(endpoint, PHI_QUEUE_SHUTDOWN_SEQUENCE, PHI_STATUS_OK) < 0)
{
LogErrorFmt("Failed to send queue shutdown acknowledgement: %s", strerror(errno));
return -1;
}
LogInfo("Queue shutdown acknowledgement sent");
return 0;
}
return run_result; return run_result;
} }
+4
View File
@@ -157,6 +157,7 @@ typedef enum PhiFormat
PHI_FORMAT_R32G32B32A32_SINT = 108, PHI_FORMAT_R32G32B32A32_SINT = 108,
PHI_FORMAT_R32G32B32A32_SFLOAT = 109, PHI_FORMAT_R32G32B32A32_SFLOAT = 109,
PHI_FORMAT_B10G11R11_UFLOAT_PACK32 = 122, PHI_FORMAT_B10G11R11_UFLOAT_PACK32 = 122,
PHI_FORMAT_E5B9G9R9_UFLOAT_PACK32 = 123,
PHI_FORMAT_D16_UNORM = 124, PHI_FORMAT_D16_UNORM = 124,
PHI_FORMAT_X8_D24_UNORM_PACK32 = 125, PHI_FORMAT_X8_D24_UNORM_PACK32 = 125,
PHI_FORMAT_D32_SFLOAT = 126, PHI_FORMAT_D32_SFLOAT = 126,
@@ -164,6 +165,9 @@ typedef enum PhiFormat
PHI_FORMAT_D16_UNORM_S8_UINT = 128, PHI_FORMAT_D16_UNORM_S8_UINT = 128,
PHI_FORMAT_D24_UNORM_S8_UINT = 129, PHI_FORMAT_D24_UNORM_S8_UINT = 129,
PHI_FORMAT_D32_SFLOAT_S8_UINT = 130, PHI_FORMAT_D32_SFLOAT_S8_UINT = 130,
PHI_FORMAT_A4R4G4B4_UNORM_PACK16 = 1000340000,
PHI_FORMAT_A4B4G4R4_UNORM_PACK16 = 1000340001,
} PhiFormat; } PhiFormat;
typedef struct PhiMessageHeader typedef struct PhiMessageHeader