Skip to content
Draft
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
54 changes: 48 additions & 6 deletions TargetBridge-Receiver/TBReceiverC/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,16 @@ LDFLAGS += $(shell $(PKG) --libs libavcodec libavutil libswscale sdl2)

UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
LDFLAGS += -framework ApplicationServices -framework AppKit -framework CoreFoundation -framework CoreGraphics -framework CoreText -framework VideoToolbox -framework CoreMedia -framework CoreVideo -framework IOSurface -framework CoreServices -framework CoreAudio -framework SystemConfiguration
LDFLAGS += -framework ApplicationServices -framework AppKit -framework CoreFoundation -framework CoreGraphics -framework CoreText -framework VideoToolbox -framework CoreMedia -framework CoreVideo -framework IOSurface -framework CoreServices -framework CoreAudio -framework SystemConfiguration -framework Metal -framework QuartzCore -lc++
endif

C_SRC = src/main.c src/net.c src/decoder.c src/display.c src/input_queue.c src/receiver_profile.c src/tb_i18n.c
C_SRC = src/main.c src/net.c src/decoder.c src/display.c src/input_queue.c src/receiver_profile.c src/renderer_policy.c src/tb_i18n.c
OBJC_SRC = src/tb_gesture_bridge.m src/tb_display_tweaks.m
OBJ = $(C_SRC:.c=.o) $(OBJC_SRC:.m=.o)
OBJCXX_SRC =
ifeq ($(UNAME_S),Darwin)
OBJCXX_SRC += src/tb_native_metal_renderer.m
endif
OBJ = $(C_SRC:.c=.o) $(OBJC_SRC:.m=.o) $(OBJCXX_SRC:.m=.o)
BIN = tbreceiver

all: $(BIN)
Expand All @@ -50,8 +54,11 @@ $(BIN): $(OBJ)
%.o: %.m
$(CC) $(CFLAGS) -fobjc-arc -c -o $@ $<

src/tb_native_metal_renderer.o: src/tb_native_metal_renderer.m src/tb_native_metal_renderer.h
$(CC) $(filter-out -std=c11,$(CFLAGS)) -std=c++17 -x objective-c++ -fobjc-arc -c -o $@ $<

clean:
rm -f $(OBJ) $(BIN) $(TEST_BIN) $(TEST_INPUT_QUEUE_BIN) $(TEST_RECEIVER_PROFILE_BIN)
rm -f $(OBJ) $(BIN) $(TEST_BIN) $(TEST_INPUT_QUEUE_BIN) $(TEST_RECEIVER_PROFILE_BIN) $(TEST_RENDERER_POLICY_BIN) $(TEST_NATIVE_METAL_BIN) $(BENCH_NATIVE_METAL_BIN) $(BENCH_OPENGL_BIN)

# Unit tests for the packet parser. net.c is pure POSIX, so this needs no
# ffmpeg/SDL/pkgconf — it runs anywhere, including CI, with no hardware.
Expand All @@ -63,6 +70,14 @@ endif
TEST_BIN = test_net_parser
TEST_INPUT_QUEUE_BIN = test_input_queue
TEST_RECEIVER_PROFILE_BIN = test_receiver_profile
TEST_RENDERER_POLICY_BIN = test_renderer_policy
TEST_NATIVE_METAL_BIN = test_native_metal_renderer
BENCH_NATIVE_METAL_BIN = benchmark_native_metal_renderer
BENCH_OPENGL_BIN = benchmark_sdl_opengl_renderer
TEST_PLATFORM_BINS =
ifeq ($(UNAME_S),Darwin)
TEST_PLATFORM_BINS += $(TEST_NATIVE_METAL_BIN)
endif

test_net_parser: tests/test_net_parser.c src/net.c src/net.h src/proto.h
$(CC) $(TEST_CFLAGS) -Isrc tests/test_net_parser.c src/net.c $(TEST_LDFLAGS) -o $@
Expand All @@ -73,9 +88,36 @@ $(TEST_INPUT_QUEUE_BIN): tests/test_input_queue.c src/input_queue.c src/input_qu
$(TEST_RECEIVER_PROFILE_BIN): tests/test_receiver_profile.c src/receiver_profile.c src/receiver_profile.h
$(CC) $(TEST_CFLAGS) -Isrc tests/test_receiver_profile.c src/receiver_profile.c -o $@

test: $(TEST_BIN) $(TEST_INPUT_QUEUE_BIN) $(TEST_RECEIVER_PROFILE_BIN)
$(TEST_RENDERER_POLICY_BIN): tests/test_renderer_policy.c src/renderer_policy.c src/renderer_policy.h
$(CC) $(TEST_CFLAGS) -Isrc tests/test_renderer_policy.c src/renderer_policy.c -lm -o $@

$(TEST_NATIVE_METAL_BIN): tests/test_native_metal_renderer.m src/tb_native_metal_renderer.m src/tb_native_metal_renderer.h
$(CC) -O2 -g -Wall -Wextra -Isrc -std=c++17 -x objective-c++ -fobjc-arc \
tests/test_native_metal_renderer.m src/tb_native_metal_renderer.m \
-framework AppKit -framework CoreVideo -framework IOSurface -framework Metal -framework QuartzCore -lc++ -o $@

$(BENCH_NATIVE_METAL_BIN): benchmarks/benchmark_native_metal_renderer.m src/tb_native_metal_renderer.m src/tb_native_metal_renderer.h src/renderer_policy.c src/renderer_policy.h
$(CC) -O2 -g -Wall -Wextra -Isrc -std=c++17 -x objective-c++ -fobjc-arc \
benchmarks/benchmark_native_metal_renderer.m src/tb_native_metal_renderer.m src/renderer_policy.c \
-framework AppKit -framework CoreVideo -framework IOSurface -framework Metal -framework QuartzCore -lc++ -o $@

benchmark-metal: $(BENCH_NATIVE_METAL_BIN)
./$(BENCH_NATIVE_METAL_BIN)

$(BENCH_OPENGL_BIN): benchmarks/benchmark_sdl_opengl_renderer.c
$(CC) -O2 -g -Wall -Wextra $(shell $(PKG) --cflags sdl2) \
benchmarks/benchmark_sdl_opengl_renderer.c $(shell $(PKG) --libs sdl2) -o $@

benchmark-opengl: $(BENCH_OPENGL_BIN)
./$(BENCH_OPENGL_BIN)

test: $(TEST_BIN) $(TEST_INPUT_QUEUE_BIN) $(TEST_RECEIVER_PROFILE_BIN) $(TEST_RENDERER_POLICY_BIN) $(TEST_PLATFORM_BINS)
./$(TEST_BIN)
./$(TEST_INPUT_QUEUE_BIN)
./$(TEST_RECEIVER_PROFILE_BIN)
./$(TEST_RENDERER_POLICY_BIN)
ifeq ($(UNAME_S),Darwin)
./$(TEST_NATIVE_METAL_BIN)
endif

.PHONY: all clean test
.PHONY: all clean test benchmark-metal benchmark-opengl
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#import "renderer_policy.h"
#import "tb_native_metal_renderer.h"

#import <AppKit/AppKit.h>
#import <CoreVideo/CoreVideo.h>
#import <Metal/Metal.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static CVPixelBufferRef make_frame(size_t width, size_t height) {
CVPixelBufferRef pixelBuffer = NULL;
CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{
(__bridge NSString *)kCVPixelBufferIOSurfacePropertiesKey: @{},
(__bridge NSString *)kCVPixelBufferMetalCompatibilityKey: @YES
};
CVReturn status = CVPixelBufferCreate(
kCFAllocatorDefault,
width,
height,
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,
attributes,
&pixelBuffer);
if (status != kCVReturnSuccess || !pixelBuffer) return NULL;

if (CVPixelBufferLockBaseAddress(pixelBuffer, 0) == kCVReturnSuccess) {
memset(CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0),
128,
CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0) *
CVPixelBufferGetHeightOfPlane(pixelBuffer, 0));
memset(CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1),
128,
CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1) *
CVPixelBufferGetHeightOfPlane(pixelBuffer, 1));
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
}
CVBufferSetAttachment(pixelBuffer,
kCVImageBufferColorPrimariesKey,
kCVImageBufferColorPrimaries_P3_D65,
kCVAttachmentMode_ShouldPropagate);
CVBufferSetAttachment(pixelBuffer,
kCVImageBufferTransferFunctionKey,
kCVImageBufferTransferFunction_ITU_R_709_2,
kCVAttachmentMode_ShouldPropagate);
CVBufferSetAttachment(pixelBuffer,
kCVImageBufferYCbCrMatrixKey,
kCVImageBufferYCbCrMatrix_ITU_R_709_2,
kCVAttachmentMode_ShouldPropagate);
return pixelBuffer;
}

int main(int argc, const char *argv[]) {
@autoreleasepool {
const int width = argc > 1 ? atoi(argv[1]) : 4096;
const int height = argc > 2 ? atoi(argv[2]) : 2304;
const int frameCount = argc > 3 ? atoi(argv[3]) : 180;
const int targetFPS = argc > 4 ? atoi(argv[4]) : 60;
if (width <= 0 || height <= 0 || frameCount < 120 || targetFPS <= 0) {
fprintf(stderr, "usage: benchmark_native_metal_renderer [width height frames>=120 fps]\n");
return 64;
}
if (!MTLCreateSystemDefaultDevice()) {
fprintf(stderr, "TB_METAL_BENCHMARK result=unavailable reason=no-metal-device\n");
return 69;
}

[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
const CGFloat windowWidth = MAX(1.0, (CGFloat)width / 2.0);
const CGFloat windowHeight = MAX(1.0, (CGFloat)height / 2.0);
NSWindow *window = [[NSWindow alloc]
initWithContentRect:NSMakeRect(0, 0, windowWidth, windowHeight)
styleMask:NSWindowStyleMaskBorderless
backing:NSBackingStoreBuffered
defer:NO];
window.title = @"TargetBridge Metal Benchmark";
/* Command-line AppKit harnesses own this window through ARC. Avoid the
* legacy close-time self-release, which would otherwise release it a
* second time when the autorelease pool drains. */
window.releasedWhenClosed = NO;
window.alphaValue = 0.02;
[window orderFront:nil];
[[NSRunLoop currentRunLoop]
runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];

CVPixelBufferRef pixelBuffer = make_frame((size_t)width, (size_t)height);
void *renderer = tb_native_metal_create();
if (!pixelBuffer || !renderer) {
fprintf(stderr, "TB_METAL_BENCHMARK result=failed reason=setup\n");
if (pixelBuffer) CVPixelBufferRelease(pixelBuffer);
if (renderer) tb_native_metal_destroy(renderer);
[window close];
return 70;
}

const CFTimeInterval started = CACurrentMediaTime();
double submitTimeMsTotal = 0.0;
double submitTimeMsMax = 0.0;
for (int frame = 0; frame < frameCount; frame++) {
const CFTimeInterval submitStarted = CACurrentMediaTime();
(void)tb_native_metal_render_nv12(
renderer,
pixelBuffer,
(frame * 13) % width,
(frame * 7) % height,
width,
height,
1,
0,
0);
const double submitTimeMs =
(CACurrentMediaTime() - submitStarted) * 1000.0;
submitTimeMsTotal += submitTimeMs;
if (submitTimeMs > submitTimeMsMax) submitTimeMsMax = submitTimeMs;
const CFTimeInterval target = started +
(CFTimeInterval)(frame + 1) / (CFTimeInterval)targetFPS;
const CFTimeInterval delay = target - CACurrentMediaTime();
if (delay > 0) {
[[NSRunLoop currentRunLoop]
runUntilDate:[NSDate dateWithTimeIntervalSinceNow:delay]];
}
}

struct tb_native_metal_stats stats;
const CFTimeInterval completionDeadline = CACurrentMediaTime() + 3.0;
do {
tb_native_metal_get_stats(renderer, &stats);
if (stats.completed_frames >= stats.submitted_frames) break;
[[NSRunLoop currentRunLoop]
runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
} while (CACurrentMediaTime() < completionDeadline);

tb_native_metal_get_stats(renderer, &stats);
const struct tb_renderer_health_sample sample = {
stats.submitted_frames,
stats.completed_frames,
stats.dropped_frames,
stats.gpu_time_ms_total
};
const struct tb_renderer_health_result health =
tb_renderer_evaluate_health(&sample);
const double elapsed = CACurrentMediaTime() - started;
const char *decision = health.decision == TB_RENDERER_HEALTH_KEEP_METAL
? "metal"
: health.decision == TB_RENDERER_HEALTH_FALLBACK_OPENGL
? "opengl"
: "insufficient-sample";
printf(
"TB_METAL_BENCHMARK result=%s size=%dx%d requested=%d targetFPS=%d "
"elapsed=%.3fs submitted=%llu completed=%llu dropped=%llu "
"submitAvg=%.3fms submitMax=%.3fms gpuAvg=%.3fms gpuMax=%.3fms "
"color=%s\n",
decision,
width,
height,
frameCount,
targetFPS,
elapsed,
(unsigned long long)stats.submitted_frames,
(unsigned long long)stats.completed_frames,
(unsigned long long)stats.dropped_frames,
submitTimeMsTotal / (double)frameCount,
submitTimeMsMax,
health.gpu_average_ms,
stats.gpu_time_ms_max,
tb_native_metal_color_space_name(renderer));

CVPixelBufferRelease(pixelBuffer);
tb_native_metal_destroy(renderer);
[window close];
return health.decision == TB_RENDERER_HEALTH_KEEP_METAL ? 0 : 2;
}
}
Loading
Loading