Skip to content

ruby: Add source frame scaling via screen functions #1508

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
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
3 changes: 3 additions & 0 deletions ares/ares/node/video/screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ auto Screen::setScale(f64 scaleX, f64 scaleY) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_scaleX = scaleX;
_scaleY = scaleY;
platform->setScale(scaleX, scaleY);
}

auto Screen::setAspect(f64 aspectX, f64 aspectY) -> void {
Expand Down Expand Up @@ -160,13 +161,15 @@ auto Screen::setProgressive(bool progressiveDouble) -> void {
_interlace = false;
_progressive = true;
_progressiveDouble = progressiveDouble;
platform->setProgressive(progressiveDouble);
}

auto Screen::setInterlace(bool interlaceField) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_progressive = false;
_interlace = true;
_interlaceField = interlaceField;
platform->setInterlace(interlaceField);
}

auto Screen::attach(Node::Video::Sprite sprite) -> void {
Expand Down
3 changes: 3 additions & 0 deletions ares/ares/platform.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ struct Platform {
virtual auto status(string_view message) -> void {}
virtual auto video(Node::Video::Screen, const u32* data, u32 pitch, u32 width, u32 height) -> void {}
virtual auto refreshRateHint(double refreshRate) -> void {}
virtual auto setScale(f64 scaleX, f64 scaleY) -> void {}
virtual auto setInterlace(bool interlaceField) -> void {}
virtual auto setProgressive(bool progressivedouble) -> void {}
virtual auto audio(Node::Audio::Stream) -> void {}
virtual auto input(Node::Input::Input) -> void {}
virtual auto cheat(u32 addr) -> maybe<u32> { return nothing; }
Expand Down
12 changes: 12 additions & 0 deletions desktop-ui/program/platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,18 @@ auto Program::refreshRateHint(double refreshRate) -> void {
ruby::video.refreshRateHint(refreshRate);
}

auto Program::setScale(f64 scaleX, f64 scaleY) -> void {
ruby::video.setScale(scaleX, scaleY);
}

auto Program::setInterlace(bool interlaceField) -> void {
ruby::video.setInterlace(interlaceField);
}

auto Program::setProgressive(bool progressiveDouble) -> void {
ruby::video.setProgressive(progressiveDouble);
}

auto Program::audio(ares::Node::Audio::Stream node) -> void {
if(!streams) return;

Expand Down
3 changes: 3 additions & 0 deletions desktop-ui/program/program.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ struct Program : ares::Platform {
auto status(string_view message) -> void override;
auto video(ares::Node::Video::Screen, const u32* data, u32 pitch, u32 width, u32 height) -> void override;
auto refreshRateHint(double refreshRate) -> void override;
auto setScale(f64 scaleX, f64 scaleY) -> void override;
auto setInterlace(bool interlaceField) -> void override;
auto setProgressive(bool progressiveDouble) -> void override;
auto audio(ares::Node::Audio::Stream) -> void override;
auto input(ares::Node::Input::Input) -> void override;
auto cheat(u32 address) -> maybe<u32> override;
Expand Down
123 changes: 103 additions & 20 deletions ruby/video/metal/metal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,70 @@ struct VideoMetal : VideoDriver, Metal {
_outputX = (width - outputWidth) / 2;
_outputY = (height - outputHeight) / 2;
}

auto resizeSourceBuffers() {
for (int i = 0; i < kMaxSourceBuffersInFlight; i++) {
if (sourceWidth < 1 || sourceHeight < 1) {
_sourceTextures[i] = nullptr;
continue;
}
MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor new];
textureDescriptor.pixelFormat = MTLPixelFormatBGRA8Unorm;
textureDescriptor.width = sourceWidth;
textureDescriptor.height = sourceHeight;
textureDescriptor.usage = MTLTextureUsageRenderTarget|MTLTextureUsageShaderRead;

_sourceTextures[i] = [_device newTextureWithDescriptor:textureDescriptor];
}

f64 newWidth = sourceWidth * _scaleX;
f64 newHeight = sourceHeight * _scaleY;
if (_interlace) {
newHeight = sourceHeight;
}
for (int i = 0; i < kMaxSourceBuffersInFlight; i++) {
if (newWidth < 1 || newHeight < 1) {
_finalSourceTextures[i] = nullptr;
continue;
}
MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor new];
textureDescriptor.pixelFormat = MTLPixelFormatBGRA8Unorm;
textureDescriptor.width = newWidth;
textureDescriptor.height = newHeight;
textureDescriptor.usage = MTLTextureUsageRenderTarget|MTLTextureUsageShaderRead;

_finalSourceTextures[i] = [_device newTextureWithDescriptor:textureDescriptor];
}
NSLog(@"Resized final source textures to %lf, %lf", newWidth, newHeight);
}

auto setScale(f64 scaleX, f64 scaleY) -> void override {
if (scaleX != _scaleX || scaleY != scaleY) {
_scaleX = scaleX;
_scaleY = scaleY;
dispatch_async(_renderQueue, ^{
resizeSourceBuffers();
});
}
}

auto setInterlace(bool interlaceField) -> void override {
if (_interlace) return;
_interlace = true;
_progressive = false;
dispatch_async(_renderQueue, ^{
resizeSourceBuffers();
});
}

auto setProgressive(bool progressiveDouble) -> void override {
if (_progressive) return;
_interlace = false;
_progressive = true;
dispatch_async(_renderQueue, ^{
resizeSourceBuffers();
});
}

auto acquire(u32*& data, u32& pitch, u32 width, u32 height) -> bool override {
if (sourceWidth != width || sourceHeight != height) {
Expand All @@ -225,21 +289,10 @@ struct VideoMetal : VideoDriver, Metal {

bytesPerRow = sourceWidth * sizeof(u32);
if (bytesPerRow < 16) bytesPerRow = 16;

for (int i = 0; i < kMaxSourceBuffersInFlight; i++) {
if (sourceWidth < 1 || sourceHeight < 1) {
_sourceTextures[i] = nullptr;
continue;
}
MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor new];
textureDescriptor.pixelFormat = MTLPixelFormatBGRA8Unorm;
textureDescriptor.width = sourceWidth;
textureDescriptor.height = sourceHeight;
textureDescriptor.usage = MTLTextureUsageRenderTarget|MTLTextureUsageShaderRead;

_sourceTextures[i] = [_device newTextureWithDescriptor:textureDescriptor];
}

dispatch_async(_renderQueue, ^{
resizeSourceBuffers();
});
}
pitch = sourceWidth * sizeof(u32);
return data = buffer;
Expand Down Expand Up @@ -312,6 +365,7 @@ struct VideoMetal : VideoDriver, Metal {
auto index = frameCount % kMaxSourceBuffersInFlight;

auto sourceTexture = _sourceTextures[index];
auto finalSourceTexture = _finalSourceTextures[index];

[sourceTexture replaceRegion:MTLRegionMake2D(0, 0, sourceWidth, sourceHeight) mipmapLevel:0 withBytes:buffer bytesPerRow:bytesPerRow];

Expand All @@ -325,18 +379,18 @@ struct VideoMetal : VideoDriver, Metal {
/// assurances that we won't block the emulation thread in the worst case system conditions.
if ((_blocking && !_vrrIsSupported) || !_threaded) {
dispatch_sync(_renderQueue, ^{
outputHelper(width, height, sourceTexture);
outputHelper(width, height, sourceTexture, finalSourceTexture);
});
} else {
dispatch_async(_renderQueue, ^{
outputHelper(width, height, sourceTexture);
outputHelper(width, height, sourceTexture, finalSourceTexture);
});
}
}
}

private:
auto outputHelper(u32 width, u32 height, id<MTLTexture> sourceTexture) -> void {
auto outputHelper(u32 width, u32 height, id<MTLTexture> sourceTexture, id<MTLTexture> finalSourceTexture) -> void {
/// Uses two render passes (plus librashader's render passes). The first render pass samples the source texture,
/// consisting of the pixel buffer from the emulator, onto a texture the same size as our eventual output,
/// `_renderTargetTexture`. Then it calls into librashader, which performs postprocessing onto the same
Expand All @@ -355,7 +409,7 @@ struct VideoMetal : VideoDriver, Metal {
dispatch_semaphore_signal(block_sema);
}];

_renderToTextureRenderPassDescriptor.colorAttachments[0].texture = _renderTargetTexture;
_renderToTextureRenderPassDescriptor.colorAttachments[0].texture = finalSourceTexture;

if (_renderToTextureRenderPassDescriptor != nil) {

Expand All @@ -365,7 +419,7 @@ struct VideoMetal : VideoDriver, Metal {

[renderEncoder setRenderPipelineState:_renderToTextureRenderPipeline];

[renderEncoder setViewport:(MTLViewport){0, 0, (double)width, (double)height, -1.0, 1.0}];
[renderEncoder setViewport:(MTLViewport){0, 0, (double)finalSourceTexture.width, (double)finalSourceTexture.height, -1.0, 1.0}];

[renderEncoder setVertexBuffer:_vertexBuffer
offset:0
Expand All @@ -382,7 +436,36 @@ struct VideoMetal : VideoDriver, Metal {
[renderEncoder endEncoding];

if (_filterChain) {
_libra.mtl_filter_chain_frame(&_filterChain, commandBuffer, frameCount, sourceTexture, _libraViewport, _renderTargetTexture, nil, nil);
_libra.mtl_filter_chain_frame(&_filterChain, commandBuffer, frameCount, finalSourceTexture, _libraViewport, _renderTargetTexture, nil, nil);
} else {

_renderToTextureRenderPassDescriptor.colorAttachments[0].texture = _renderTargetTexture;

if (_renderToTextureRenderPassDescriptor != nil) {

id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:_renderToTextureRenderPassDescriptor];

_renderToTextureRenderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;

[renderEncoder setRenderPipelineState:_renderToTextureRenderPipeline];

[renderEncoder setViewport:(MTLViewport){0, 0, (double)width, (double)height, -1.0, 1.0}];

[renderEncoder setVertexBuffer:_vertexBuffer
offset:0
atIndex:0];

[renderEncoder setVertexBytes:&_viewportSize
length:sizeof(_viewportSize)
atIndex:MetalVertexInputIndexViewportSize];

[renderEncoder setFragmentTexture:finalSourceTexture atIndex:0];

[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6];

[renderEncoder endEncoding];

}
}

//this call will block the current thread/queue if a drawable is not yet available
Expand Down
10 changes: 10 additions & 0 deletions ruby/video/metal/metal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ struct Metal {
auto initialize(const string& shader) -> bool;
auto terminate() -> void;
auto refreshRateHint(double refreshRate) -> void;
auto setScale(f64 scaleX, f64 scaleY) -> void;
auto setInterlace(bool interlaceField) -> void;
auto setProgressive(bool progressiveDouble) -> void;
auto resizeSourceBuffers() -> void;

auto size(u32 width, u32 height) -> void;
auto release() -> void;
Expand All @@ -54,6 +58,8 @@ struct Metal {

u32 sourceWidth = 0;
u32 sourceHeight = 0;
f64 _scaleX = 1;
f64 _scaleY = 1;
u32 bytesPerRow = 0;

u32 outputWidth = 0;
Expand All @@ -62,6 +68,9 @@ struct Metal {
double _outputY = 0;
u32 depth = 0;

bool _interlace = false;
bool _progressive = false;

dispatch_queue_t _renderQueue = nullptr;

CGFloat _viewWidth = 0;
Expand Down Expand Up @@ -89,6 +98,7 @@ struct Metal {

id<MTLBuffer> _vertexBuffer;
id<MTLTexture> _sourceTextures[kMaxSourceBuffersInFlight];
id<MTLTexture> _finalSourceTextures[kMaxSourceBuffersInFlight];
MTLVertexDescriptor *_mtlVertexDescriptor;

MTLRenderPassDescriptor *_renderToTextureRenderPassDescriptor;
Expand Down
15 changes: 15 additions & 0 deletions ruby/video/video.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,21 @@ auto Video::refreshRateHint(double refreshRate) -> void {
instance->refreshRateHint(refreshRate);
}

auto Video::setScale(f64 scaleX, f64 scaleY) -> void {
lock_guard<recursive_mutex> lock(mutex);
instance->setScale(scaleX, scaleY);
}

auto Video::setInterlace(bool interlaceField) -> void {
lock_guard<recursive_mutex> lock(mutex);
instance->setInterlace(interlaceField);
}

auto Video::setProgressive(bool progressiveDouble) -> void {
lock_guard<recursive_mutex> lock(mutex);
instance->setProgressive(progressiveDouble);
}

//

auto Video::focused() -> bool {
Expand Down
6 changes: 6 additions & 0 deletions ruby/video/video.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ struct VideoDriver {
virtual auto setFormat(string format) -> bool { return true; }
virtual auto setShader(string shader) -> bool { return true; }
virtual auto refreshRateHint(double refreshRate) -> void {}
virtual auto setScale(f64 scaleX, f64 scaleY) -> void {}
virtual auto setInterlace(bool interlaceField) -> void {}
virtual auto setProgressive(bool progressiveDouble) -> void {}

virtual auto focused() -> bool { return true; }
virtual auto clear() -> void {}
Expand Down Expand Up @@ -129,6 +132,9 @@ struct Video {
auto setFormat(string format) -> bool;
auto setShader(string shader) -> bool;
auto refreshRateHint(double refreshRate) -> void;
auto setScale(f64 scaleX, f64 scaleY) -> void;
auto setInterlace(bool interlaceField) -> void;
auto setProgressive(bool progressiveDouble) -> void;

auto focused() -> bool;
auto clear() -> void;
Expand Down