SGB resolution support (Cocoa only so far)
This commit is contained in:
parent
6ba5cfbeef
commit
634a54c046
@ -225,8 +225,9 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
|
|||||||
- (void) run
|
- (void) run
|
||||||
{
|
{
|
||||||
running = true;
|
running = true;
|
||||||
GB_set_pixels_output(&gb, self.view.pixels);
|
|
||||||
self.view.gb = &gb;
|
self.view.gb = &gb;
|
||||||
|
[self.view screenSizeChanged];
|
||||||
|
GB_set_pixels_output(&gb, self.view.pixels);
|
||||||
GB_set_sample_rate(&gb, 96000);
|
GB_set_sample_rate(&gb, 96000);
|
||||||
self.audioClient = [[GBAudioClient alloc] initWithRendererBlock:^(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer) {
|
self.audioClient = [[GBAudioClient alloc] initWithRendererBlock:^(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer) {
|
||||||
GB_apu_copy_buffer(&gb, buffer, nFrames);
|
GB_apu_copy_buffer(&gb, buffer, nFrames);
|
||||||
@ -565,21 +566,24 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
|
|||||||
if (fullScreen) {
|
if (fullScreen) {
|
||||||
return newFrame;
|
return newFrame;
|
||||||
}
|
}
|
||||||
|
size_t width = GB_get_screen_width(&gb),
|
||||||
|
height = GB_get_screen_height(&gb);
|
||||||
|
|
||||||
NSRect rect = window.contentView.frame;
|
NSRect rect = window.contentView.frame;
|
||||||
|
|
||||||
int titlebarSize = window.contentView.superview.frame.size.height - rect.size.height;
|
int titlebarSize = window.contentView.superview.frame.size.height - rect.size.height;
|
||||||
int step = 160 / [[window screen] backingScaleFactor];
|
int step = width / [[window screen] backingScaleFactor];
|
||||||
|
|
||||||
rect.size.width = floor(rect.size.width / step) * step + step;
|
rect.size.width = floor(rect.size.width / step) * step + step;
|
||||||
rect.size.height = rect.size.width / 10 * 9 + titlebarSize;
|
rect.size.height = rect.size.width * height / width + titlebarSize;
|
||||||
|
|
||||||
if (rect.size.width > newFrame.size.width) {
|
if (rect.size.width > newFrame.size.width) {
|
||||||
rect.size.width = 160;
|
rect.size.width = width;
|
||||||
rect.size.height = 144 + titlebarSize;
|
rect.size.height = height + titlebarSize;
|
||||||
}
|
}
|
||||||
else if (rect.size.height > newFrame.size.height) {
|
else if (rect.size.height > newFrame.size.height) {
|
||||||
rect.size.width = 160;
|
rect.size.width = width;
|
||||||
rect.size.height = 144 + titlebarSize;
|
rect.size.height = height + titlebarSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
rect.origin = window.frame.origin;
|
rect.origin = window.frame.origin;
|
||||||
|
@ -2,5 +2,5 @@
|
|||||||
|
|
||||||
@interface GBGLShader : NSObject
|
@interface GBGLShader : NSObject
|
||||||
- (instancetype)initWithName:(NSString *) shaderName;
|
- (instancetype)initWithName:(NSString *) shaderName;
|
||||||
- (void) renderBitmap: (void *)bitmap previous:(void*) previous inSize:(NSSize)size scale: (double) scale;
|
- (void) renderBitmap: (void *)bitmap previous:(void*) previous sized:(NSSize)srcSize inSize:(NSSize)dstSize scale: (double) scale;
|
||||||
@end
|
@end
|
||||||
|
@ -79,19 +79,19 @@ void main(void) {\n\
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) renderBitmap: (void *)bitmap previous:(void*) previous inSize:(NSSize)size scale: (double) scale
|
- (void) renderBitmap: (void *)bitmap previous:(void*) previous sized:(NSSize)srcSize inSize:(NSSize)dstSize scale: (double) scale
|
||||||
{
|
{
|
||||||
glUseProgram(program);
|
glUseProgram(program);
|
||||||
glUniform2f(resolution_uniform, size.width * scale, size.height * scale);
|
glUniform2f(resolution_uniform, dstSize.width * scale, dstSize.height * scale);
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
glBindTexture(GL_TEXTURE_2D, texture);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 160, 144, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, srcSize.width, srcSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
|
||||||
glUniform1i(texture_uniform, 0);
|
glUniform1i(texture_uniform, 0);
|
||||||
glUniform1i(mix_previous_uniform, previous != NULL);
|
glUniform1i(mix_previous_uniform, previous != NULL);
|
||||||
if (previous) {
|
if (previous) {
|
||||||
glActiveTexture(GL_TEXTURE1);
|
glActiveTexture(GL_TEXTURE1);
|
||||||
glBindTexture(GL_TEXTURE_2D, previous_texture);
|
glBindTexture(GL_TEXTURE_2D, previous_texture);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 160, 144, 0, GL_RGBA, GL_UNSIGNED_BYTE, previous);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, srcSize.width, srcSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, previous);
|
||||||
glUniform1i(previous_texture_uniform, 1);
|
glUniform1i(previous_texture_uniform, 1);
|
||||||
}
|
}
|
||||||
glBindFragDataLocation(program, 0, "frag_color");
|
glBindFragDataLocation(program, 0, "frag_color");
|
||||||
|
@ -13,18 +13,11 @@
|
|||||||
double scale = self.window.backingScaleFactor;
|
double scale = self.window.backingScaleFactor;
|
||||||
glViewport(0, 0, self.bounds.size.width * scale, self.bounds.size.height * scale);
|
glViewport(0, 0, self.bounds.size.width * scale, self.bounds.size.height * scale);
|
||||||
|
|
||||||
if (gbview.shouldBlendFrameWithPrevious) {
|
|
||||||
[self.shader renderBitmap:gbview.currentBuffer
|
[self.shader renderBitmap:gbview.currentBuffer
|
||||||
previous:gbview.previousBuffer
|
previous:gbview.shouldBlendFrameWithPrevious? gbview.previousBuffer : NULL
|
||||||
|
sized:NSMakeSize(GB_get_screen_width(gbview.gb), GB_get_screen_height(gbview.gb))
|
||||||
inSize:self.bounds.size
|
inSize:self.bounds.size
|
||||||
scale:scale];
|
scale:scale];
|
||||||
}
|
|
||||||
else {
|
|
||||||
[self.shader renderBitmap:gbview.currentBuffer
|
|
||||||
previous:NULL
|
|
||||||
inSize:self.bounds.size
|
|
||||||
scale:scale];
|
|
||||||
}
|
|
||||||
glFlush();
|
glFlush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,4 +13,5 @@
|
|||||||
- (void) createInternalView;
|
- (void) createInternalView;
|
||||||
- (uint32_t *)currentBuffer;
|
- (uint32_t *)currentBuffer;
|
||||||
- (uint32_t *)previousBuffer;
|
- (uint32_t *)previousBuffer;
|
||||||
|
- (void)screenSizeChanged;
|
||||||
@end
|
@end
|
||||||
|
@ -44,9 +44,8 @@
|
|||||||
|
|
||||||
- (void) _init
|
- (void) _init
|
||||||
{
|
{
|
||||||
image_buffers[0] = malloc(160 * 144 * 4);
|
[self screenSizeChanged];
|
||||||
image_buffers[1] = malloc(160 * 144 * 4);
|
|
||||||
image_buffers[2] = malloc(160 * 144 * 4);
|
|
||||||
_shouldBlendFrameWithPrevious = 1;
|
_shouldBlendFrameWithPrevious = 1;
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ratioKeepingChanged) name:@"GBAspectChanged" object:nil];
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ratioKeepingChanged) name:@"GBAspectChanged" object:nil];
|
||||||
tracking_area = [ [NSTrackingArea alloc] initWithRect:(NSRect){}
|
tracking_area = [ [NSTrackingArea alloc] initWithRect:(NSRect){}
|
||||||
@ -60,6 +59,24 @@
|
|||||||
self.internalView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
self.internalView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)screenSizeChanged
|
||||||
|
{
|
||||||
|
if (!_gb) return;
|
||||||
|
if (image_buffers[0]) free(image_buffers[0]);
|
||||||
|
if (image_buffers[1]) free(image_buffers[1]);
|
||||||
|
if (image_buffers[2]) free(image_buffers[2]);
|
||||||
|
|
||||||
|
size_t buffer_size = sizeof(image_buffers[0][0]) * GB_get_screen_width(_gb) * GB_get_screen_height(_gb);
|
||||||
|
|
||||||
|
image_buffers[0] = malloc(buffer_size);
|
||||||
|
image_buffers[1] = malloc(buffer_size);
|
||||||
|
image_buffers[2] = malloc(buffer_size);
|
||||||
|
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self setFrame:self.superview.frame];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
- (void) ratioKeepingChanged
|
- (void) ratioKeepingChanged
|
||||||
{
|
{
|
||||||
[self setFrame:self.superview.frame];
|
[self setFrame:self.superview.frame];
|
||||||
@ -112,14 +129,16 @@
|
|||||||
frame = self.superview.frame;
|
frame = self.superview.frame;
|
||||||
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"GBAspectRatioUnkept"]) {
|
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"GBAspectRatioUnkept"]) {
|
||||||
double ratio = frame.size.width / frame.size.height;
|
double ratio = frame.size.width / frame.size.height;
|
||||||
if (ratio >= 160.0/144.0) {
|
double width = GB_get_screen_width(_gb);
|
||||||
double new_width = round(frame.size.height / 144.0 * 160.0);
|
double height = GB_get_screen_height(_gb);
|
||||||
|
if (ratio >= width / height) {
|
||||||
|
double new_width = round(frame.size.height / height * width);
|
||||||
frame.origin.x = floor((frame.size.width - new_width) / 2);
|
frame.origin.x = floor((frame.size.width - new_width) / 2);
|
||||||
frame.size.width = new_width;
|
frame.size.width = new_width;
|
||||||
frame.origin.y = 0;
|
frame.origin.y = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
double new_height = round(frame.size.width / 160.0 * 144.0);
|
double new_height = round(frame.size.width / width * height);
|
||||||
frame.origin.y = floor((frame.size.height - new_height) / 2);
|
frame.origin.y = floor((frame.size.height - new_height) / 2);
|
||||||
frame.size.height = new_height;
|
frame.size.height = new_height;
|
||||||
frame.origin.x = 0;
|
frame.origin.x = 0;
|
||||||
|
@ -1,14 +1,5 @@
|
|||||||
#import "GBViewMetal.h"
|
#import "GBViewMetal.h"
|
||||||
|
|
||||||
#define WIDTH 160
|
|
||||||
#define HEIGHT 144
|
|
||||||
#define PITCH (160 * 4)
|
|
||||||
|
|
||||||
static const MTLRegion region = {
|
|
||||||
{0, 0, 0}, // MTLOrigin
|
|
||||||
{WIDTH, HEIGHT, 1} // MTLSize
|
|
||||||
};
|
|
||||||
|
|
||||||
static const vector_float2 rect[] =
|
static const vector_float2 rect[] =
|
||||||
{
|
{
|
||||||
{-1, -1},
|
{-1, -1},
|
||||||
@ -37,6 +28,29 @@ static const vector_float2 rect[] =
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void) allocateTextures
|
||||||
|
{
|
||||||
|
if (!device) return;
|
||||||
|
if (!self.gb) return;
|
||||||
|
|
||||||
|
MTLTextureDescriptor *texture_descriptor = [[MTLTextureDescriptor alloc] init];
|
||||||
|
|
||||||
|
texture_descriptor.pixelFormat = MTLPixelFormatRGBA8Unorm;
|
||||||
|
|
||||||
|
texture_descriptor.width = GB_get_screen_width(self.gb);
|
||||||
|
texture_descriptor.height = GB_get_screen_height(self.gb);
|
||||||
|
|
||||||
|
texture = [device newTextureWithDescriptor:texture_descriptor];
|
||||||
|
previous_texture = [device newTextureWithDescriptor:texture_descriptor];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)screenSizeChanged
|
||||||
|
{
|
||||||
|
[super screenSizeChanged];
|
||||||
|
[self allocateTextures];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)createInternalView
|
- (void)createInternalView
|
||||||
{
|
{
|
||||||
MTKView *view = [[MTKView alloc] initWithFrame:self.frame device:(device = MTLCreateSystemDefaultDevice())];
|
MTKView *view = [[MTKView alloc] initWithFrame:self.frame device:(device = MTLCreateSystemDefaultDevice())];
|
||||||
@ -44,15 +58,7 @@ static const vector_float2 rect[] =
|
|||||||
self.internalView = view;
|
self.internalView = view;
|
||||||
view.paused = YES;
|
view.paused = YES;
|
||||||
|
|
||||||
MTLTextureDescriptor *texture_descriptor = [[MTLTextureDescriptor alloc] init];
|
[self allocateTextures];
|
||||||
|
|
||||||
texture_descriptor.pixelFormat = MTLPixelFormatRGBA8Unorm;
|
|
||||||
|
|
||||||
texture_descriptor.width = WIDTH;
|
|
||||||
texture_descriptor.height = HEIGHT;
|
|
||||||
|
|
||||||
texture = [device newTextureWithDescriptor:texture_descriptor];
|
|
||||||
previous_texture = [device newTextureWithDescriptor:texture_descriptor];
|
|
||||||
|
|
||||||
vertices = [device newBufferWithBytes:rect
|
vertices = [device newBufferWithBytes:rect
|
||||||
length:sizeof(rect)
|
length:sizeof(rect)
|
||||||
@ -134,15 +140,21 @@ static const vector_float2 rect[] =
|
|||||||
- (void)drawInMTKView:(nonnull MTKView *)view
|
- (void)drawInMTKView:(nonnull MTKView *)view
|
||||||
{
|
{
|
||||||
if (!(view.window.occlusionState & NSWindowOcclusionStateVisible)) return;
|
if (!(view.window.occlusionState & NSWindowOcclusionStateVisible)) return;
|
||||||
|
|
||||||
|
MTLRegion region = {
|
||||||
|
{0, 0, 0}, // MTLOrigin
|
||||||
|
{texture.width, texture.height, 1} // MTLSize
|
||||||
|
};
|
||||||
|
|
||||||
[texture replaceRegion:region
|
[texture replaceRegion:region
|
||||||
mipmapLevel:0
|
mipmapLevel:0
|
||||||
withBytes:[self currentBuffer]
|
withBytes:[self currentBuffer]
|
||||||
bytesPerRow:PITCH];
|
bytesPerRow:texture.width * 4];
|
||||||
if ([self shouldBlendFrameWithPrevious]) {
|
if ([self shouldBlendFrameWithPrevious]) {
|
||||||
[previous_texture replaceRegion:region
|
[previous_texture replaceRegion:region
|
||||||
mipmapLevel:0
|
mipmapLevel:0
|
||||||
withBytes:[self previousBuffer]
|
withBytes:[self previousBuffer]
|
||||||
bytesPerRow:PITCH];
|
bytesPerRow:texture.width * 4];
|
||||||
}
|
}
|
||||||
|
|
||||||
MTLRenderPassDescriptor *render_pass_descriptor = view.currentRenderPassDescriptor;
|
MTLRenderPassDescriptor *render_pass_descriptor = view.currentRenderPassDescriptor;
|
||||||
|
@ -132,6 +132,13 @@ static void display_vblank(GB_gameboy_t *gb)
|
|||||||
|
|
||||||
if (!gb->disable_rendering && ((!(gb->io_registers[GB_IO_LCDC] & 0x80) || gb->stopped) || gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON)) {
|
if (!gb->disable_rendering && ((!(gb->io_registers[GB_IO_LCDC] & 0x80) || gb->stopped) || gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON)) {
|
||||||
/* LCD is off, set screen to white or black (if LCD is on in stop mode) */
|
/* LCD is off, set screen to white or black (if LCD is on in stop mode) */
|
||||||
|
if (gb->sgb_screen_buffer) {
|
||||||
|
uint8_t color = (gb->io_registers[GB_IO_LCDC] & 0x80) && gb->stopped ? 0 : 0xFF;
|
||||||
|
for (unsigned i = 0; i < WIDTH * LINES; i++) {
|
||||||
|
gb ->sgb_screen_buffer[i] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
uint32_t color = (gb->io_registers[GB_IO_LCDC] & 0x80) && gb->stopped ?
|
uint32_t color = (gb->io_registers[GB_IO_LCDC] & 0x80) && gb->stopped ?
|
||||||
gb->rgb_encode_callback(gb, 0, 0, 0) :
|
gb->rgb_encode_callback(gb, 0, 0, 0) :
|
||||||
gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
|
gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
|
||||||
@ -139,7 +146,11 @@ static void display_vblank(GB_gameboy_t *gb)
|
|||||||
gb ->screen[i] = color;
|
gb ->screen[i] = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GB_is_sgb(gb)) {
|
||||||
|
GB_sgb_render(gb);
|
||||||
|
}
|
||||||
gb->vblank_callback(gb);
|
gb->vblank_callback(gb);
|
||||||
GB_timing_sync(gb);
|
GB_timing_sync(gb);
|
||||||
}
|
}
|
||||||
@ -375,8 +386,13 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
|
|||||||
if (!gb->cgb_mode) {
|
if (!gb->cgb_mode) {
|
||||||
pixel = ((gb->io_registers[GB_IO_BGP] >> (pixel << 1)) & 3);
|
pixel = ((gb->io_registers[GB_IO_BGP] >> (pixel << 1)) & 3);
|
||||||
}
|
}
|
||||||
|
if (gb->sgb_screen_buffer) {
|
||||||
|
gb->sgb_screen_buffer[gb->position_in_line + gb->current_line * WIDTH] = pixel;
|
||||||
|
}
|
||||||
|
else {
|
||||||
gb->screen[gb->position_in_line + gb->current_line * WIDTH] = gb->background_palettes_rgb[fifo_item->palette * 4 + pixel];
|
gb->screen[gb->position_in_line + gb->current_line * WIDTH] = gb->background_palettes_rgb[fifo_item->palette * 4 + pixel];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (draw_oam) {
|
if (draw_oam) {
|
||||||
uint8_t pixel = oam_fifo_item->pixel;
|
uint8_t pixel = oam_fifo_item->pixel;
|
||||||
@ -384,8 +400,13 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
|
|||||||
/* Todo: Verify access timings */
|
/* Todo: Verify access timings */
|
||||||
pixel = ((gb->io_registers[oam_fifo_item->palette? GB_IO_OBP1 : GB_IO_OBP0] >> (pixel << 1)) & 3);
|
pixel = ((gb->io_registers[oam_fifo_item->palette? GB_IO_OBP1 : GB_IO_OBP0] >> (pixel << 1)) & 3);
|
||||||
}
|
}
|
||||||
|
if (gb->sgb_screen_buffer) {
|
||||||
|
gb->sgb_screen_buffer[gb->position_in_line + gb->current_line * WIDTH] =pixel;
|
||||||
|
}
|
||||||
|
else {
|
||||||
gb->screen[gb->position_in_line + gb->current_line * WIDTH] = gb->sprite_palettes_rgb[oam_fifo_item->palette * 4 + pixel];
|
gb->screen[gb->position_in_line + gb->current_line * WIDTH] = gb->sprite_palettes_rgb[oam_fifo_item->palette * 4 + pixel];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gb->position_in_line++;
|
gb->position_in_line++;
|
||||||
}
|
}
|
||||||
|
25
Core/gb.c
25
Core/gb.c
@ -139,6 +139,9 @@ void GB_free(GB_gameboy_t *gb)
|
|||||||
if (gb->breakpoints) {
|
if (gb->breakpoints) {
|
||||||
free(gb->breakpoints);
|
free(gb->breakpoints);
|
||||||
}
|
}
|
||||||
|
if (gb->sgb_screen_buffer) {
|
||||||
|
free(gb->sgb_screen_buffer);
|
||||||
|
}
|
||||||
#ifndef DISABLE_DEBUGGER
|
#ifndef DISABLE_DEBUGGER
|
||||||
GB_debugger_clear_symbols(gb);
|
GB_debugger_clear_symbols(gb);
|
||||||
#endif
|
#endif
|
||||||
@ -642,6 +645,18 @@ void GB_reset(GB_gameboy_t *gb)
|
|||||||
|
|
||||||
gb->sgb_player_count = 1;
|
gb->sgb_player_count = 1;
|
||||||
|
|
||||||
|
if (GB_is_sgb(gb)) {
|
||||||
|
if (!gb->sgb_screen_buffer) {
|
||||||
|
gb->sgb_screen_buffer = malloc(160 * 144);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (gb->sgb_screen_buffer) {
|
||||||
|
free(gb->sgb_screen_buffer);
|
||||||
|
gb->sgb_screen_buffer = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Todo: Ugly, fixme, see comment in the timer state machine */
|
/* Todo: Ugly, fixme, see comment in the timer state machine */
|
||||||
gb->div_state = 3;
|
gb->div_state = 3;
|
||||||
|
|
||||||
@ -744,3 +759,13 @@ uint32_t GB_get_clock_rate(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
return CPU_FREQUENCY * gb->clock_multiplier;
|
return CPU_FREQUENCY * gb->clock_multiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t GB_get_screen_width(GB_gameboy_t *gb)
|
||||||
|
{
|
||||||
|
return (gb && GB_is_sgb(gb))? 256 : 160;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GB_get_screen_height(GB_gameboy_t *gb)
|
||||||
|
{
|
||||||
|
return (gb && GB_is_sgb(gb))? 224 : 144;
|
||||||
|
}
|
||||||
|
@ -475,6 +475,9 @@ struct GB_gameboy_internal_s {
|
|||||||
bool sgb_ready_for_write;
|
bool sgb_ready_for_write;
|
||||||
bool sgb_ready_for_stop;
|
bool sgb_ready_for_stop;
|
||||||
bool sgb_disable_commands;
|
bool sgb_disable_commands;
|
||||||
|
|
||||||
|
/* Screen buffer */
|
||||||
|
uint8_t *sgb_screen_buffer;
|
||||||
/* Multiplayer Input */
|
/* Multiplayer Input */
|
||||||
uint8_t sgb_player_count, sgb_current_player;
|
uint8_t sgb_player_count, sgb_current_player;
|
||||||
);
|
);
|
||||||
@ -678,4 +681,7 @@ uint32_t GB_get_clock_rate(GB_gameboy_t *gb);
|
|||||||
#endif
|
#endif
|
||||||
void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier);
|
void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier);
|
||||||
|
|
||||||
|
size_t GB_get_screen_width(GB_gameboy_t *gb);
|
||||||
|
size_t GB_get_screen_height(GB_gameboy_t *gb);
|
||||||
|
|
||||||
#endif /* GB_h */
|
#endif /* GB_h */
|
||||||
|
26
Core/sgb.c
26
Core/sgb.c
@ -127,3 +127,29 @@ void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GB_sgb_render(GB_gameboy_t *gb)
|
||||||
|
{
|
||||||
|
if (!gb->screen || !gb->rgb_encode_callback) return;
|
||||||
|
|
||||||
|
uint32_t border = gb->rgb_encode_callback(gb, 0x9c, 0x9c, 0xa5);
|
||||||
|
uint32_t colors[] = {
|
||||||
|
gb->rgb_encode_callback(gb, 0xf7, 0xe7, 0xc6),
|
||||||
|
gb->rgb_encode_callback(gb, 0xd6, 0x8e, 0x49),
|
||||||
|
gb->rgb_encode_callback(gb, 0xa6, 0x37, 0x25),
|
||||||
|
gb->rgb_encode_callback(gb, 0x33, 0x1e, 0x50)
|
||||||
|
};
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < 256 * 224; i++) {
|
||||||
|
gb->screen[i] = border;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t *output = &gb->screen[48 + 39 * 256];
|
||||||
|
uint8_t *input = gb->sgb_screen_buffer;
|
||||||
|
for (unsigned y = 0; y < 144; y++) {
|
||||||
|
for (unsigned x = 0; x < 160; x++) {
|
||||||
|
*(output++) = colors[*(input++) & 3];
|
||||||
|
}
|
||||||
|
output += 256 - 160;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value);
|
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value);
|
||||||
|
void GB_sgb_render(GB_gameboy_t *gb);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -21,6 +21,7 @@ void main()
|
|||||||
vec2 position = gl_FragCoord.xy - origin;
|
vec2 position = gl_FragCoord.xy - origin;
|
||||||
position /= output_resolution;
|
position /= output_resolution;
|
||||||
position.y = 1 - position.y;
|
position.y = 1 - position.y;
|
||||||
|
vec2 input_resolution = textureSize(image, 0);
|
||||||
|
|
||||||
if (mix_previous) {
|
if (mix_previous) {
|
||||||
frag_color = mix(scale(image, position, input_resolution, output_resolution),
|
frag_color = mix(scale(image, position, input_resolution, output_resolution),
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#include <metal_math>
|
#include <metal_math>
|
||||||
|
|
||||||
using namespace metal;
|
using namespace metal;
|
||||||
constant float2 input_resolution = float2(160, 144);
|
|
||||||
|
|
||||||
/* For GLSL compatibility */
|
/* For GLSL compatibility */
|
||||||
typedef float2 vec2;
|
typedef float2 vec2;
|
||||||
@ -49,6 +48,8 @@ fragment float4 fragment_shader(rasterizer_data in [[stage_in]],
|
|||||||
constant bool *mix_previous [[ buffer(0) ]],
|
constant bool *mix_previous [[ buffer(0) ]],
|
||||||
constant float2 *output_resolution [[ buffer(1) ]])
|
constant float2 *output_resolution [[ buffer(1) ]])
|
||||||
{
|
{
|
||||||
|
float2 input_resolution = float2(image.get_width(), image.get_height());
|
||||||
|
|
||||||
in.texcoords.y = 1 - in.texcoords.y;
|
in.texcoords.y = 1 - in.texcoords.y;
|
||||||
if (*mix_previous) {
|
if (*mix_previous) {
|
||||||
return mix(scale(image, in.texcoords, input_resolution, *output_resolution),
|
return mix(scale(image, in.texcoords, input_resolution, *output_resolution),
|
||||||
|
Loading…
Reference in New Issue
Block a user