Add accurate frame blending option
This commit is contained in:
parent
e94e7cc501
commit
5ecb845662
@ -1,5 +1,6 @@
|
|||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
#include "GBButtons.h"
|
#include "GBButtons.h"
|
||||||
|
#include "GBView.h"
|
||||||
#include <Core/gb.h>
|
#include <Core/gb.h>
|
||||||
#import <Carbon/Carbon.h>
|
#import <Carbon/Carbon.h>
|
||||||
|
|
||||||
@ -36,6 +37,7 @@
|
|||||||
@"GBColorCorrection": @(GB_COLOR_CORRECTION_EMULATE_HARDWARE),
|
@"GBColorCorrection": @(GB_COLOR_CORRECTION_EMULATE_HARDWARE),
|
||||||
@"GBHighpassFilter": @(GB_HIGHPASS_REMOVE_DC_OFFSET),
|
@"GBHighpassFilter": @(GB_HIGHPASS_REMOVE_DC_OFFSET),
|
||||||
@"GBRewindLength": @(10),
|
@"GBRewindLength": @(10),
|
||||||
|
@"GBFrameBlendingMode": @([defaults boolForKey:@"DisableFrameBlending"]? GB_FRAME_BLENDING_MODE_DISABLED : GB_FRAME_BLENDING_MODE_ACCURATE),
|
||||||
|
|
||||||
@"GBDMGModel": @(GB_MODEL_DMG_B),
|
@"GBDMGModel": @(GB_MODEL_DMG_B),
|
||||||
@"GBCGBModel": @(GB_MODEL_CGB_E),
|
@"GBCGBModel": @(GB_MODEL_CGB_E),
|
||||||
|
@ -492,7 +492,7 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
|
|
||||||
self.consoleOutput.textContainerInset = NSMakeSize(4, 4);
|
self.consoleOutput.textContainerInset = NSMakeSize(4, 4);
|
||||||
[self.view becomeFirstResponder];
|
[self.view becomeFirstResponder];
|
||||||
self.view.shouldBlendFrameWithPrevious = ![[NSUserDefaults standardUserDefaults] boolForKey:@"DisableFrameBlending"];
|
self.view.frameBlendingMode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBFrameBlendingMode"];
|
||||||
CGRect window_frame = self.mainWindow.frame;
|
CGRect window_frame = self.mainWindow.frame;
|
||||||
window_frame.size.width = MAX([[NSUserDefaults standardUserDefaults] integerForKey:@"LastWindowWidth"],
|
window_frame.size.width = MAX([[NSUserDefaults standardUserDefaults] integerForKey:@"LastWindowWidth"],
|
||||||
window_frame.size.width);
|
window_frame.size.width);
|
||||||
@ -521,6 +521,11 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
name:@"GBColorCorrectionChanged"
|
name:@"GBColorCorrectionChanged"
|
||||||
object:nil];
|
object:nil];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
|
selector:@selector(updateFrameBlendingMode)
|
||||||
|
name:@"GBFrameBlendingModeChanged"
|
||||||
|
object:nil];
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
selector:@selector(updatePalette)
|
selector:@selector(updatePalette)
|
||||||
name:@"GBColorPaletteChanged"
|
name:@"GBColorPaletteChanged"
|
||||||
@ -677,12 +682,6 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
[[NSUserDefaults standardUserDefaults] setBool:!self.audioClient.isPlaying forKey:@"Mute"];
|
[[NSUserDefaults standardUserDefaults] setBool:!self.audioClient.isPlaying forKey:@"Mute"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)toggleBlend:(id)sender
|
|
||||||
{
|
|
||||||
self.view.shouldBlendFrameWithPrevious ^= YES;
|
|
||||||
[[NSUserDefaults standardUserDefaults] setBool:!self.view.shouldBlendFrameWithPrevious forKey:@"DisableFrameBlending"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)anItem
|
- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)anItem
|
||||||
{
|
{
|
||||||
if([anItem action] == @selector(mute:)) {
|
if([anItem action] == @selector(mute:)) {
|
||||||
@ -695,9 +694,6 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
else if ([anItem action] == @selector(reset:) && anItem.tag != MODEL_NONE) {
|
else if ([anItem action] == @selector(reset:) && anItem.tag != MODEL_NONE) {
|
||||||
[(NSMenuItem*)anItem setState:anItem.tag == current_model];
|
[(NSMenuItem*)anItem setState:anItem.tag == current_model];
|
||||||
}
|
}
|
||||||
else if ([anItem action] == @selector(toggleBlend:)) {
|
|
||||||
[(NSMenuItem*)anItem setState:self.view.shouldBlendFrameWithPrevious];
|
|
||||||
}
|
|
||||||
else if ([anItem action] == @selector(interrupt:)) {
|
else if ([anItem action] == @selector(interrupt:)) {
|
||||||
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"DeveloperMode"]) {
|
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"DeveloperMode"]) {
|
||||||
return false;
|
return false;
|
||||||
@ -1617,6 +1613,11 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void) updateFrameBlendingMode
|
||||||
|
{
|
||||||
|
self.view.frameBlendingMode = (GB_frame_blending_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBFrameBlendingMode"];
|
||||||
|
}
|
||||||
|
|
||||||
- (void) updateRewindLength
|
- (void) updateRewindLength
|
||||||
{
|
{
|
||||||
[self performAtomicBlock:^{
|
[self performAtomicBlock:^{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "GBView.h"
|
||||||
|
|
||||||
@interface GBGLShader : NSObject
|
@interface GBGLShader : NSObject
|
||||||
- (instancetype)initWithName:(NSString *) shaderName;
|
- (instancetype)initWithName:(NSString *) shaderName;
|
||||||
- (void) renderBitmap: (void *)bitmap previous:(void*) previous sized:(NSSize)srcSize inSize:(NSSize)dstSize scale: (double) scale;
|
- (void) renderBitmap: (void *)bitmap previous:(void*) previous sized:(NSSize)srcSize inSize:(NSSize)dstSize scale: (double) scale withBlendingMode: (GB_frame_blending_mode_t)blendingMode;
|
||||||
@end
|
@end
|
||||||
|
@ -21,7 +21,7 @@ void main(void) {\n\
|
|||||||
GLuint resolution_uniform;
|
GLuint resolution_uniform;
|
||||||
GLuint texture_uniform;
|
GLuint texture_uniform;
|
||||||
GLuint previous_texture_uniform;
|
GLuint previous_texture_uniform;
|
||||||
GLuint mix_previous_uniform;
|
GLuint frame_blending_mode_uniform;
|
||||||
|
|
||||||
GLuint position_attribute;
|
GLuint position_attribute;
|
||||||
GLuint texture;
|
GLuint texture;
|
||||||
@ -70,7 +70,7 @@ void main(void) {\n\
|
|||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
previous_texture_uniform = glGetUniformLocation(program, "previous_image");
|
previous_texture_uniform = glGetUniformLocation(program, "previous_image");
|
||||||
|
|
||||||
mix_previous_uniform = glGetUniformLocation(program, "mix_previous");
|
frame_blending_mode_uniform = glGetUniformLocation(program, "frame_blending_mode");
|
||||||
|
|
||||||
// Configure OpenGL
|
// Configure OpenGL
|
||||||
[self configureOpenGL];
|
[self configureOpenGL];
|
||||||
@ -79,7 +79,7 @@ void main(void) {\n\
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) renderBitmap: (void *)bitmap previous:(void*) previous sized:(NSSize)srcSize inSize:(NSSize)dstSize scale: (double) scale
|
- (void) renderBitmap: (void *)bitmap previous:(void*) previous sized:(NSSize)srcSize inSize:(NSSize)dstSize scale: (double) scale withBlendingMode:(GB_frame_blending_mode_t)blendingMode
|
||||||
{
|
{
|
||||||
glUseProgram(program);
|
glUseProgram(program);
|
||||||
glUniform2f(resolution_uniform, dstSize.width * scale, dstSize.height * scale);
|
glUniform2f(resolution_uniform, dstSize.width * scale, dstSize.height * scale);
|
||||||
@ -87,8 +87,8 @@ void main(void) {\n\
|
|||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
glBindTexture(GL_TEXTURE_2D, texture);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, srcSize.width, srcSize.height, 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(frame_blending_mode_uniform, blendingMode);
|
||||||
if (previous) {
|
if (blendingMode) {
|
||||||
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, srcSize.width, srcSize.height, 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);
|
||||||
|
@ -14,10 +14,11 @@
|
|||||||
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);
|
||||||
|
|
||||||
[self.shader renderBitmap:gbview.currentBuffer
|
[self.shader renderBitmap:gbview.currentBuffer
|
||||||
previous:gbview.shouldBlendFrameWithPrevious? gbview.previousBuffer : NULL
|
previous:gbview.frameBlendingMode? gbview.previousBuffer : NULL
|
||||||
sized:NSMakeSize(GB_get_screen_width(gbview.gb), GB_get_screen_height(gbview.gb))
|
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
|
||||||
|
withBlendingMode:gbview.frameBlendingMode];
|
||||||
glFlush();
|
glFlush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
@property (strong) IBOutlet NSButton *aspectRatioCheckbox;
|
@property (strong) IBOutlet NSButton *aspectRatioCheckbox;
|
||||||
@property (strong) IBOutlet NSPopUpButton *highpassFilterPopupButton;
|
@property (strong) IBOutlet NSPopUpButton *highpassFilterPopupButton;
|
||||||
@property (strong) IBOutlet NSPopUpButton *colorCorrectionPopupButton;
|
@property (strong) IBOutlet NSPopUpButton *colorCorrectionPopupButton;
|
||||||
|
@property (strong) IBOutlet NSPopUpButton *frameBlendingModePopupButton;
|
||||||
@property (strong) IBOutlet NSPopUpButton *colorPalettePopupButton;
|
@property (strong) IBOutlet NSPopUpButton *colorPalettePopupButton;
|
||||||
@property (strong) IBOutlet NSPopUpButton *displayBorderPopupButton;
|
@property (strong) IBOutlet NSPopUpButton *displayBorderPopupButton;
|
||||||
@property (strong) IBOutlet NSPopUpButton *rewindPopupButton;
|
@property (strong) IBOutlet NSPopUpButton *rewindPopupButton;
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
NSPopUpButton *_graphicsFilterPopupButton;
|
NSPopUpButton *_graphicsFilterPopupButton;
|
||||||
NSPopUpButton *_highpassFilterPopupButton;
|
NSPopUpButton *_highpassFilterPopupButton;
|
||||||
NSPopUpButton *_colorCorrectionPopupButton;
|
NSPopUpButton *_colorCorrectionPopupButton;
|
||||||
|
NSPopUpButton *_frameBlendingModePopupButton;
|
||||||
NSPopUpButton *_colorPalettePopupButton;
|
NSPopUpButton *_colorPalettePopupButton;
|
||||||
NSPopUpButton *_displayBorderPopupButton;
|
NSPopUpButton *_displayBorderPopupButton;
|
||||||
NSPopUpButton *_rewindPopupButton;
|
NSPopUpButton *_rewindPopupButton;
|
||||||
@ -87,6 +88,18 @@
|
|||||||
return _colorCorrectionPopupButton;
|
return _colorCorrectionPopupButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setFrameBlendingModePopupButton:(NSPopUpButton *)frameBlendingModePopupButton
|
||||||
|
{
|
||||||
|
_frameBlendingModePopupButton = frameBlendingModePopupButton;
|
||||||
|
NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBFrameBlendingMode"];
|
||||||
|
[_frameBlendingModePopupButton selectItemAtIndex:mode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSPopUpButton *)frameBlendingModePopupButton
|
||||||
|
{
|
||||||
|
return _frameBlendingModePopupButton;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setColorPalettePopupButton:(NSPopUpButton *)colorPalettePopupButton
|
- (void)setColorPalettePopupButton:(NSPopUpButton *)colorPalettePopupButton
|
||||||
{
|
{
|
||||||
_colorPalettePopupButton = colorPalettePopupButton;
|
_colorPalettePopupButton = colorPalettePopupButton;
|
||||||
@ -223,6 +236,13 @@
|
|||||||
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
|
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
|
||||||
forKey:@"GBColorCorrection"];
|
forKey:@"GBColorCorrection"];
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorCorrectionChanged" object:nil];
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorCorrectionChanged" object:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (IBAction)franeBlendingModeChanged:(id)sender
|
||||||
|
{
|
||||||
|
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
|
||||||
|
forKey:@"GBFrameBlendingMode"];
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBFrameBlendingModeChanged" object:nil];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +251,6 @@
|
|||||||
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
|
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
|
||||||
forKey:@"GBColorPalette"];
|
forKey:@"GBColorPalette"];
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorPaletteChanged" object:nil];
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorPaletteChanged" object:nil];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)displayBorderChanged:(id)sender
|
- (IBAction)displayBorderChanged:(id)sender
|
||||||
@ -239,7 +258,6 @@
|
|||||||
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedItem].tag)
|
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedItem].tag)
|
||||||
forKey:@"GBBorderMode"];
|
forKey:@"GBBorderMode"];
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBBorderModeChanged" object:nil];
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBBorderModeChanged" object:nil];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)rewindLengthChanged:(id)sender
|
- (IBAction)rewindLengthChanged:(id)sender
|
||||||
|
@ -2,11 +2,19 @@
|
|||||||
#include <Core/gb.h>
|
#include <Core/gb.h>
|
||||||
#import "GBJoystickListener.h"
|
#import "GBJoystickListener.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GB_FRAME_BLENDING_MODE_DISABLED,
|
||||||
|
GB_FRAME_BLENDING_MODE_SIMPLE,
|
||||||
|
GB_FRAME_BLENDING_MODE_ACCURATE,
|
||||||
|
GB_FRAME_BLENDING_MODE_ACCURATE_EVEN = GB_FRAME_BLENDING_MODE_ACCURATE,
|
||||||
|
GB_FRAME_BLENDING_MODE_ACCURATE_ODD,
|
||||||
|
} GB_frame_blending_mode_t;
|
||||||
|
|
||||||
@interface GBView<GBJoystickListener> : NSView
|
@interface GBView<GBJoystickListener> : NSView
|
||||||
- (void) flip;
|
- (void) flip;
|
||||||
- (uint32_t *) pixels;
|
- (uint32_t *) pixels;
|
||||||
@property GB_gameboy_t *gb;
|
@property GB_gameboy_t *gb;
|
||||||
@property (nonatomic) BOOL shouldBlendFrameWithPrevious;
|
@property (nonatomic) GB_frame_blending_mode_t frameBlendingMode;
|
||||||
@property (getter=isMouseHidingEnabled) BOOL mouseHidingEnabled;
|
@property (getter=isMouseHidingEnabled) BOOL mouseHidingEnabled;
|
||||||
@property bool isRewinding;
|
@property bool isRewinding;
|
||||||
@property NSView *internalView;
|
@property NSView *internalView;
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
bool underclockKeyDown;
|
bool underclockKeyDown;
|
||||||
double clockMultiplier;
|
double clockMultiplier;
|
||||||
NSEventModifierFlags previousModifiers;
|
NSEventModifierFlags previousModifiers;
|
||||||
|
GB_frame_blending_mode_t _frameBlendingMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (instancetype)alloc
|
+ (instancetype)alloc
|
||||||
@ -44,7 +45,6 @@
|
|||||||
|
|
||||||
- (void) _init
|
- (void) _init
|
||||||
{
|
{
|
||||||
_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){}
|
||||||
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect
|
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect
|
||||||
@ -79,15 +79,26 @@
|
|||||||
[self setFrame:self.superview.frame];
|
[self setFrame:self.superview.frame];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) setShouldBlendFrameWithPrevious:(BOOL)shouldBlendFrameWithPrevious
|
- (void) setFrameBlendingMode:(GB_frame_blending_mode_t)frameBlendingMode
|
||||||
{
|
{
|
||||||
_shouldBlendFrameWithPrevious = shouldBlendFrameWithPrevious;
|
_frameBlendingMode = frameBlendingMode;
|
||||||
[self setNeedsDisplay:YES];
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (GB_frame_blending_mode_t)frameBlendingMode
|
||||||
|
{
|
||||||
|
if (_frameBlendingMode == GB_FRAME_BLENDING_MODE_ACCURATE) {
|
||||||
|
if (GB_is_sgb(_gb)) {
|
||||||
|
return GB_FRAME_BLENDING_MODE_SIMPLE;
|
||||||
|
}
|
||||||
|
return GB_is_odd_frame(_gb)? GB_FRAME_BLENDING_MODE_ACCURATE_ODD : GB_FRAME_BLENDING_MODE_ACCURATE_EVEN;
|
||||||
|
}
|
||||||
|
return _frameBlendingMode;
|
||||||
|
}
|
||||||
- (unsigned char) numberOfBuffers
|
- (unsigned char) numberOfBuffers
|
||||||
{
|
{
|
||||||
return _shouldBlendFrameWithPrevious? 3 : 2;
|
return _frameBlendingMode? 3 : 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
|
@ -15,7 +15,7 @@ static const vector_float2 rect[] =
|
|||||||
id<MTLBuffer> vertices;
|
id<MTLBuffer> vertices;
|
||||||
id<MTLRenderPipelineState> pipeline_state;
|
id<MTLRenderPipelineState> pipeline_state;
|
||||||
id<MTLCommandQueue> command_queue;
|
id<MTLCommandQueue> command_queue;
|
||||||
id<MTLBuffer> mix_previous_buffer;
|
id<MTLBuffer> frame_blending_mode_buffer;
|
||||||
id<MTLBuffer> output_resolution_buffer;
|
id<MTLBuffer> output_resolution_buffer;
|
||||||
vector_float2 output_resolution;
|
vector_float2 output_resolution;
|
||||||
}
|
}
|
||||||
@ -23,7 +23,7 @@ static const vector_float2 rect[] =
|
|||||||
+ (bool)isSupported
|
+ (bool)isSupported
|
||||||
{
|
{
|
||||||
if (MTLCopyAllDevices) {
|
if (MTLCopyAllDevices) {
|
||||||
return [MTLCopyAllDevices() count];
|
return false; //[MTLCopyAllDevices() count];
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -56,9 +56,9 @@ static const vector_float2 rect[] =
|
|||||||
length:sizeof(rect)
|
length:sizeof(rect)
|
||||||
options:MTLResourceStorageModeShared];
|
options:MTLResourceStorageModeShared];
|
||||||
|
|
||||||
static const bool default_mix_value = false;
|
static const GB_frame_blending_mode_t default_blending_mode = GB_FRAME_BLENDING_MODE_DISABLED;
|
||||||
mix_previous_buffer = [device newBufferWithBytes:&default_mix_value
|
frame_blending_mode_buffer = [device newBufferWithBytes:&default_blending_mode
|
||||||
length:sizeof(default_mix_value)
|
length:sizeof(default_blending_mode)
|
||||||
options:MTLResourceStorageModeShared];
|
options:MTLResourceStorageModeShared];
|
||||||
|
|
||||||
output_resolution_buffer = [device newBufferWithBytes:&output_resolution
|
output_resolution_buffer = [device newBufferWithBytes:&output_resolution
|
||||||
@ -147,7 +147,7 @@ static const vector_float2 rect[] =
|
|||||||
mipmapLevel:0
|
mipmapLevel:0
|
||||||
withBytes:[self currentBuffer]
|
withBytes:[self currentBuffer]
|
||||||
bytesPerRow:texture.width * 4];
|
bytesPerRow:texture.width * 4];
|
||||||
if ([self shouldBlendFrameWithPrevious]) {
|
if ([self frameBlendingMode]) {
|
||||||
[previous_texture replaceRegion:region
|
[previous_texture replaceRegion:region
|
||||||
mipmapLevel:0
|
mipmapLevel:0
|
||||||
withBytes:[self previousBuffer]
|
withBytes:[self previousBuffer]
|
||||||
@ -159,7 +159,7 @@ static const vector_float2 rect[] =
|
|||||||
|
|
||||||
if (render_pass_descriptor != nil)
|
if (render_pass_descriptor != nil)
|
||||||
{
|
{
|
||||||
*(bool *)[mix_previous_buffer contents] = [self shouldBlendFrameWithPrevious];
|
*(GB_frame_blending_mode_t *)[frame_blending_mode_buffer contents] = [self frameBlendingMode];
|
||||||
*(vector_float2 *)[output_resolution_buffer contents] = output_resolution;
|
*(vector_float2 *)[output_resolution_buffer contents] = output_resolution;
|
||||||
|
|
||||||
id<MTLRenderCommandEncoder> render_encoder =
|
id<MTLRenderCommandEncoder> render_encoder =
|
||||||
@ -176,7 +176,7 @@ static const vector_float2 rect[] =
|
|||||||
offset:0
|
offset:0
|
||||||
atIndex:0];
|
atIndex:0];
|
||||||
|
|
||||||
[render_encoder setFragmentBuffer:mix_previous_buffer
|
[render_encoder setFragmentBuffer:frame_blending_mode_buffer
|
||||||
offset:0
|
offset:0
|
||||||
atIndex:0];
|
atIndex:0];
|
||||||
|
|
||||||
|
@ -69,6 +69,7 @@
|
|||||||
<outlet property="delegate" destination="-2" id="ASc-vN-Zbq"/>
|
<outlet property="delegate" destination="-2" id="ASc-vN-Zbq"/>
|
||||||
<outlet property="displayBorderPopupButton" destination="R9D-FV-bpd" id="VfO-b4-gqH"/>
|
<outlet property="displayBorderPopupButton" destination="R9D-FV-bpd" id="VfO-b4-gqH"/>
|
||||||
<outlet property="dmgPopupButton" destination="LFw-Uk-cPR" id="KDw-i2-k4u"/>
|
<outlet property="dmgPopupButton" destination="LFw-Uk-cPR" id="KDw-i2-k4u"/>
|
||||||
|
<outlet property="frameBlendingModePopupButton" destination="lxk-db-Sxv" id="wzt-uo-TE6"/>
|
||||||
<outlet property="graphicsFilterPopupButton" destination="6pP-kK-EEC" id="LS7-HY-kHC"/>
|
<outlet property="graphicsFilterPopupButton" destination="6pP-kK-EEC" id="LS7-HY-kHC"/>
|
||||||
<outlet property="highpassFilterPopupButton" destination="T69-6N-dhT" id="0p6-4m-hb1"/>
|
<outlet property="highpassFilterPopupButton" destination="T69-6N-dhT" id="0p6-4m-hb1"/>
|
||||||
<outlet property="playerListButton" destination="gWx-7h-0xq" id="zo6-82-JId"/>
|
<outlet property="playerListButton" destination="gWx-7h-0xq" id="zo6-82-JId"/>
|
||||||
@ -80,11 +81,11 @@
|
|||||||
<point key="canvasLocation" x="183" y="354"/>
|
<point key="canvasLocation" x="183" y="354"/>
|
||||||
</window>
|
</window>
|
||||||
<customView id="sRK-wO-K6R">
|
<customView id="sRK-wO-K6R">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="292" height="267"/>
|
<rect key="frame" x="0.0" y="0.0" width="292" height="323"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T91-rh-rRp">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T91-rh-rRp">
|
||||||
<rect key="frame" x="18" y="230" width="256" height="17"/>
|
<rect key="frame" x="18" y="286" width="256" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Graphics filter:" id="pXg-WY-8Q5">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Graphics filter:" id="pXg-WY-8Q5">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
@ -93,7 +94,7 @@
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6pP-kK-EEC">
|
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6pP-kK-EEC">
|
||||||
<rect key="frame" x="30" y="198" width="234" height="26"/>
|
<rect key="frame" x="30" y="254" width="234" height="26"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<popUpButtonCell key="cell" type="push" title="Nearest neighbor (Pixelated)" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="neN-eo-LA7" id="I1w-05-lGl">
|
<popUpButtonCell key="cell" type="push" title="Nearest neighbor (Pixelated)" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="neN-eo-LA7" id="I1w-05-lGl">
|
||||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
@ -130,7 +131,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</popUpButton>
|
</popUpButton>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wc3-2K-6CD">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wc3-2K-6CD">
|
||||||
<rect key="frame" x="18" y="176" width="256" height="17"/>
|
<rect key="frame" x="18" y="232" width="256" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Color correction:" id="5Si-hz-EK3">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Color correction:" id="5Si-hz-EK3">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
@ -139,7 +140,7 @@
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VEz-N4-uP6">
|
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VEz-N4-uP6">
|
||||||
<rect key="frame" x="30" y="144" width="234" height="26"/>
|
<rect key="frame" x="30" y="200" width="234" height="26"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<popUpButtonCell key="cell" type="push" title="Disabled" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="D2J-wV-1vu" id="fNJ-Fi-yOm">
|
<popUpButtonCell key="cell" type="push" title="Disabled" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="D2J-wV-1vu" id="fNJ-Fi-yOm">
|
||||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
@ -160,6 +161,35 @@
|
|||||||
<action selector="colorCorrectionChanged:" target="QvC-M9-y7g" id="Oq4-B5-nO6"/>
|
<action selector="colorCorrectionChanged:" target="QvC-M9-y7g" id="Oq4-B5-nO6"/>
|
||||||
</connections>
|
</connections>
|
||||||
</popUpButton>
|
</popUpButton>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="MLC-Rx-FgO">
|
||||||
|
<rect key="frame" x="20" y="178" width="252" height="17"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Frame blending" id="UCa-EO-tzh">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="lxk-db-Sxv">
|
||||||
|
<rect key="frame" x="32" y="149" width="229" height="22"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<popUpButtonCell key="cell" type="push" title="Disabled" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="iHP-Yz-fiH" id="aQ6-HN-7Aj">
|
||||||
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="menu"/>
|
||||||
|
<menu key="menu" id="Q9L-qo-kF4">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Disabled" state="on" id="iHP-Yz-fiH">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Simple" id="Hxy-jw-x6E"/>
|
||||||
|
<menuItem title="Accurate" id="Aaq-uy-Csa"/>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</popUpButtonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="franeBlendingModeChanged:" target="QvC-M9-y7g" id="kE1-pm-MIp"/>
|
||||||
|
</connections>
|
||||||
|
</popUpButton>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8fG-zm-hpr">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8fG-zm-hpr">
|
||||||
<rect key="frame" x="18" y="122" width="252" height="17"/>
|
<rect key="frame" x="18" y="122" width="252" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
@ -231,7 +261,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
</subviews>
|
</subviews>
|
||||||
<point key="canvasLocation" x="-176" y="639.5"/>
|
<point key="canvasLocation" x="-176" y="667.5"/>
|
||||||
</customView>
|
</customView>
|
||||||
<customView id="ymk-46-SX7">
|
<customView id="ymk-46-SX7">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="292" height="320"/>
|
<rect key="frame" x="0.0" y="0.0" width="292" height="320"/>
|
||||||
@ -460,7 +490,7 @@
|
|||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<clipView key="contentView" focusRingType="none" ambiguous="YES" drawsBackground="NO" id="AMs-PO-nid">
|
<clipView key="contentView" focusRingType="none" ambiguous="YES" drawsBackground="NO" id="AMs-PO-nid">
|
||||||
<rect key="frame" x="1" y="1" width="238" height="209"/>
|
<rect key="frame" x="1" y="1" width="238" height="209"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<tableView focusRingType="none" appearanceType="vibrantLight" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnResizing="NO" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" id="UDd-IJ-fxX">
|
<tableView focusRingType="none" appearanceType="vibrantLight" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnResizing="NO" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" id="UDd-IJ-fxX">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="238" height="209"/>
|
<rect key="frame" x="0.0" y="0.0" width="238" height="209"/>
|
||||||
|
@ -797,6 +797,8 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gb->is_odd_frame = false;
|
||||||
|
|
||||||
if (!GB_is_cgb(gb)) {
|
if (!GB_is_cgb(gb)) {
|
||||||
GB_SLEEP(gb, display, 23, 1);
|
GB_SLEEP(gb, display, 23, 1);
|
||||||
}
|
}
|
||||||
@ -1228,6 +1230,7 @@ abort_fetching_object:
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!GB_is_sgb(gb) || gb->current_lcd_line < LINES) {
|
if (!GB_is_sgb(gb) || gb->current_lcd_line < LINES) {
|
||||||
|
gb->is_odd_frame ^= true;
|
||||||
display_vblank(gb);
|
display_vblank(gb);
|
||||||
}
|
}
|
||||||
gb->frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED;
|
gb->frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED;
|
||||||
@ -1236,6 +1239,7 @@ abort_fetching_object:
|
|||||||
else {
|
else {
|
||||||
gb->frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED;
|
gb->frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED;
|
||||||
if (!GB_is_sgb(gb) || gb->current_lcd_line < LINES) {
|
if (!GB_is_sgb(gb) || gb->current_lcd_line < LINES) {
|
||||||
|
gb->is_odd_frame ^= true;
|
||||||
display_vblank(gb);
|
display_vblank(gb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1465,3 +1469,9 @@ uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_h
|
|||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool GB_is_odd_frame(GB_gameboy_t *gb)
|
||||||
|
{
|
||||||
|
return gb->is_odd_frame;
|
||||||
|
}
|
||||||
|
@ -58,4 +58,5 @@ void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette
|
|||||||
uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_height);
|
uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_height);
|
||||||
uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color, bool for_border);
|
uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color, bool for_border);
|
||||||
void GB_set_color_correction_mode(GB_gameboy_t *gb, GB_color_correction_mode_t mode);
|
void GB_set_color_correction_mode(GB_gameboy_t *gb, GB_color_correction_mode_t mode);
|
||||||
|
bool GB_is_odd_frame(GB_gameboy_t *gb);
|
||||||
#endif /* display_h */
|
#endif /* display_h */
|
||||||
|
@ -524,6 +524,7 @@ struct GB_gameboy_internal_s {
|
|||||||
bool wy_triggered;
|
bool wy_triggered;
|
||||||
uint8_t window_tile_x;
|
uint8_t window_tile_x;
|
||||||
uint8_t lcd_x; // The LCD can go out of sync since the push signal is skipped in some cases.
|
uint8_t lcd_x; // The LCD can go out of sync since the push signal is skipped in some cases.
|
||||||
|
bool is_odd_frame;
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */
|
/* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */
|
||||||
|
43
SDL/gui.c
43
SDL/gui.c
@ -46,9 +46,22 @@ void render_texture(void *pixels, void *previous)
|
|||||||
}
|
}
|
||||||
glClearColor(0, 0, 0, 1);
|
glClearColor(0, 0, 0, 1);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
GB_frame_blending_mode_t mode = configuration.blending_mode;
|
||||||
|
if (!previous) {
|
||||||
|
mode = GB_FRAME_BLENDING_MODE_DISABLED;
|
||||||
|
}
|
||||||
|
else if (mode == GB_FRAME_BLENDING_MODE_ACCURATE) {
|
||||||
|
if (GB_is_sgb(&gb)) {
|
||||||
|
mode = GB_FRAME_BLENDING_MODE_SIMPLE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mode = GB_is_odd_frame(&gb)? GB_FRAME_BLENDING_MODE_ACCURATE_ODD : GB_FRAME_BLENDING_MODE_ACCURATE_EVEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
render_bitmap_with_shader(&shader, _pixels, previous,
|
render_bitmap_with_shader(&shader, _pixels, previous,
|
||||||
GB_get_screen_width(&gb), GB_get_screen_height(&gb),
|
GB_get_screen_width(&gb), GB_get_screen_height(&gb),
|
||||||
rect.x, rect.y, rect.w, rect.h);
|
rect.x, rect.y, rect.w, rect.h,
|
||||||
|
mode);
|
||||||
SDL_GL_SwapWindow(window);
|
SDL_GL_SwapWindow(window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,7 +104,7 @@ configuration_t configuration =
|
|||||||
.color_correction_mode = GB_COLOR_CORRECTION_EMULATE_HARDWARE,
|
.color_correction_mode = GB_COLOR_CORRECTION_EMULATE_HARDWARE,
|
||||||
.highpass_mode = GB_HIGHPASS_ACCURATE,
|
.highpass_mode = GB_HIGHPASS_ACCURATE,
|
||||||
.scaling_mode = GB_SDL_SCALING_INTEGER_FACTOR,
|
.scaling_mode = GB_SDL_SCALING_INTEGER_FACTOR,
|
||||||
.blend_frames = true,
|
.blending_mode = GB_FRAME_BLENDING_MODE_ACCURATE,
|
||||||
.rewind_length = 60 * 2,
|
.rewind_length = 60 * 2,
|
||||||
.model = MODEL_CGB
|
.model = MODEL_CGB
|
||||||
};
|
};
|
||||||
@ -600,23 +613,39 @@ const char *current_filter_name(unsigned index)
|
|||||||
return shaders[i].display_name;
|
return shaders[i].display_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void toggle_blend_frames(unsigned index)
|
static void cycle_blending_mode(unsigned index)
|
||||||
{
|
{
|
||||||
configuration.blend_frames ^= true;
|
if (configuration.blending_mode == GB_FRAME_BLENDING_MODE_ACCURATE) {
|
||||||
|
configuration.blending_mode = GB_FRAME_BLENDING_MODE_DISABLED;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
configuration.blending_mode++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *blend_frames_string(unsigned index)
|
static void cycle_blending_mode_backwards(unsigned index)
|
||||||
{
|
{
|
||||||
return configuration.blend_frames? "Enabled" : "Disabled";
|
if (configuration.blending_mode == GB_FRAME_BLENDING_MODE_DISABLED) {
|
||||||
|
configuration.blending_mode = GB_FRAME_BLENDING_MODE_ACCURATE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
configuration.blending_mode--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *blending_mode_string(unsigned index)
|
||||||
|
{
|
||||||
|
return (const char *[]){"Disabled", "Simple", "Accurate"}
|
||||||
|
[configuration.blending_mode];
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct menu_item graphics_menu[] = {
|
static const struct menu_item graphics_menu[] = {
|
||||||
{"Scaling Mode:", cycle_scaling, current_scaling_mode, cycle_scaling_backwards},
|
{"Scaling Mode:", cycle_scaling, current_scaling_mode, cycle_scaling_backwards},
|
||||||
{"Scaling Filter:", cycle_filter, current_filter_name, cycle_filter_backwards},
|
{"Scaling Filter:", cycle_filter, current_filter_name, cycle_filter_backwards},
|
||||||
{"Color Correction:", cycle_color_correction, current_color_correction_mode, cycle_color_correction_backwards},
|
{"Color Correction:", cycle_color_correction, current_color_correction_mode, cycle_color_correction_backwards},
|
||||||
|
{"Frame Blending:", cycle_blending_mode, blending_mode_string, cycle_blending_mode_backwards},
|
||||||
{"Mono Palette:", cycle_palette, current_palette, cycle_palette_backwards},
|
{"Mono Palette:", cycle_palette, current_palette, cycle_palette_backwards},
|
||||||
{"Display Border:", cycle_border_mode, current_border_mode, cycle_border_mode_backwards},
|
{"Display Border:", cycle_border_mode, current_border_mode, cycle_border_mode_backwards},
|
||||||
{"Blend Frames:", toggle_blend_frames, blend_frames_string, toggle_blend_frames},
|
|
||||||
{"Back", return_to_root_menu},
|
{"Back", return_to_root_menu},
|
||||||
{NULL,}
|
{NULL,}
|
||||||
};
|
};
|
||||||
|
@ -70,7 +70,7 @@ typedef struct {
|
|||||||
SDL_Scancode keys[9];
|
SDL_Scancode keys[9];
|
||||||
GB_color_correction_mode_t color_correction_mode;
|
GB_color_correction_mode_t color_correction_mode;
|
||||||
enum scaling_mode scaling_mode;
|
enum scaling_mode scaling_mode;
|
||||||
bool blend_frames;
|
uint8_t blending_mode;
|
||||||
|
|
||||||
GB_highpass_mode_t highpass_mode;
|
GB_highpass_mode_t highpass_mode;
|
||||||
|
|
||||||
|
@ -372,7 +372,7 @@ static void vblank(GB_gameboy_t *gb)
|
|||||||
clock_mutliplier += 1.0/16;
|
clock_mutliplier += 1.0/16;
|
||||||
GB_set_clock_multiplier(gb, clock_mutliplier);
|
GB_set_clock_multiplier(gb, clock_mutliplier);
|
||||||
}
|
}
|
||||||
if (configuration.blend_frames) {
|
if (configuration.blending_mode) {
|
||||||
render_texture(active_pixel_buffer, previous_pixel_buffer);
|
render_texture(active_pixel_buffer, previous_pixel_buffer);
|
||||||
uint32_t *temp = active_pixel_buffer;
|
uint32_t *temp = active_pixel_buffer;
|
||||||
active_pixel_buffer = previous_pixel_buffer;
|
active_pixel_buffer = previous_pixel_buffer;
|
||||||
|
@ -130,7 +130,7 @@ bool init_shader_with_name(shader_t *shader, const char *name)
|
|||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
shader->previous_texture_uniform = glGetUniformLocation(shader->program, "previous_image");
|
shader->previous_texture_uniform = glGetUniformLocation(shader->program, "previous_image");
|
||||||
|
|
||||||
shader->mix_previous_uniform = glGetUniformLocation(shader->program, "mix_previous");
|
shader->blending_mode_uniform = glGetUniformLocation(shader->program, "frame_blending_mode");
|
||||||
|
|
||||||
// Program
|
// Program
|
||||||
|
|
||||||
@ -164,7 +164,8 @@ bool init_shader_with_name(shader_t *shader, const char *name)
|
|||||||
|
|
||||||
void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous,
|
void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous,
|
||||||
unsigned source_width, unsigned source_height,
|
unsigned source_width, unsigned source_height,
|
||||||
unsigned x, unsigned y, unsigned w, unsigned h)
|
unsigned x, unsigned y, unsigned w, unsigned h,
|
||||||
|
GB_frame_blending_mode_t blending_mode)
|
||||||
{
|
{
|
||||||
glUseProgram(shader->program);
|
glUseProgram(shader->program);
|
||||||
glUniform2f(shader->origin_uniform, x, y);
|
glUniform2f(shader->origin_uniform, x, y);
|
||||||
@ -173,7 +174,7 @@ void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous,
|
|||||||
glBindTexture(GL_TEXTURE_2D, shader->texture);
|
glBindTexture(GL_TEXTURE_2D, shader->texture);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, source_width, source_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, source_width, source_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
|
||||||
glUniform1i(shader->texture_uniform, 0);
|
glUniform1i(shader->texture_uniform, 0);
|
||||||
glUniform1i(shader->mix_previous_uniform, previous != NULL);
|
glUniform1i(shader->blending_mode_uniform, previous? blending_mode : GB_FRAME_BLENDING_MODE_DISABLED);
|
||||||
if (previous) {
|
if (previous) {
|
||||||
glActiveTexture(GL_TEXTURE1);
|
glActiveTexture(GL_TEXTURE1);
|
||||||
glBindTexture(GL_TEXTURE_2D, shader->previous_texture);
|
glBindTexture(GL_TEXTURE_2D, shader->previous_texture);
|
||||||
|
13
SDL/shader.h
13
SDL/shader.h
@ -8,7 +8,7 @@ typedef struct shader_s {
|
|||||||
GLuint origin_uniform;
|
GLuint origin_uniform;
|
||||||
GLuint texture_uniform;
|
GLuint texture_uniform;
|
||||||
GLuint previous_texture_uniform;
|
GLuint previous_texture_uniform;
|
||||||
GLuint mix_previous_uniform;
|
GLuint blending_mode_uniform;
|
||||||
|
|
||||||
GLuint position_attribute;
|
GLuint position_attribute;
|
||||||
GLuint texture;
|
GLuint texture;
|
||||||
@ -16,10 +16,19 @@ typedef struct shader_s {
|
|||||||
GLuint program;
|
GLuint program;
|
||||||
} shader_t;
|
} shader_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GB_FRAME_BLENDING_MODE_DISABLED,
|
||||||
|
GB_FRAME_BLENDING_MODE_SIMPLE,
|
||||||
|
GB_FRAME_BLENDING_MODE_ACCURATE,
|
||||||
|
GB_FRAME_BLENDING_MODE_ACCURATE_EVEN = GB_FRAME_BLENDING_MODE_ACCURATE,
|
||||||
|
GB_FRAME_BLENDING_MODE_ACCURATE_ODD,
|
||||||
|
} GB_frame_blending_mode_t;
|
||||||
|
|
||||||
bool init_shader_with_name(shader_t *shader, const char *name);
|
bool init_shader_with_name(shader_t *shader, const char *name);
|
||||||
void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous,
|
void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous,
|
||||||
unsigned source_width, unsigned source_height,
|
unsigned source_width, unsigned source_height,
|
||||||
unsigned x, unsigned y, unsigned w, unsigned h);
|
unsigned x, unsigned y, unsigned w, unsigned h,
|
||||||
|
GB_frame_blending_mode_t blending_mode);
|
||||||
void free_shader(struct shader_s *shader);
|
void free_shader(struct shader_s *shader);
|
||||||
|
|
||||||
#endif /* shader_h */
|
#endif /* shader_h */
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#version 150
|
#version 150
|
||||||
uniform sampler2D image;
|
uniform sampler2D image;
|
||||||
uniform sampler2D previous_image;
|
uniform sampler2D previous_image;
|
||||||
uniform bool mix_previous;
|
uniform int frame_blending_mode;
|
||||||
|
|
||||||
uniform vec2 output_resolution;
|
uniform vec2 output_resolution;
|
||||||
uniform vec2 origin;
|
uniform vec2 origin;
|
||||||
@ -15,6 +15,15 @@ out vec4 frag_color;
|
|||||||
#line 1
|
#line 1
|
||||||
{filter}
|
{filter}
|
||||||
|
|
||||||
|
|
||||||
|
#define BLEND_BIAS (1.0/3.0)
|
||||||
|
|
||||||
|
#define DISABLED 0
|
||||||
|
#define SIMPLE 1
|
||||||
|
#define ACCURATE 2
|
||||||
|
#define ACCURATE_EVEN ACCURATE
|
||||||
|
#define ACCURATE_ODD 3
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
vec2 position = gl_FragCoord.xy - origin;
|
vec2 position = gl_FragCoord.xy - origin;
|
||||||
@ -22,11 +31,34 @@ void main()
|
|||||||
position.y = 1 - position.y;
|
position.y = 1 - position.y;
|
||||||
vec2 input_resolution = textureSize(image, 0);
|
vec2 input_resolution = textureSize(image, 0);
|
||||||
|
|
||||||
if (mix_previous) {
|
float ratio;
|
||||||
frag_color = mix(scale(image, position, input_resolution, output_resolution),
|
switch (frame_blending_mode) {
|
||||||
scale(previous_image, position, input_resolution, output_resolution), 0.5);
|
default:
|
||||||
|
case DISABLED:
|
||||||
|
frag_color = scale(image, position, input_resolution, output_resolution);
|
||||||
|
return;
|
||||||
|
case SIMPLE:
|
||||||
|
ratio = 0.5;
|
||||||
|
break;
|
||||||
|
case ACCURATE_EVEN:
|
||||||
|
if ((int(position.y * input_resolution.y) & 1) == 0) {
|
||||||
|
ratio = BLEND_BIAS;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
frag_color = scale(image, position, input_resolution, output_resolution);
|
ratio = 1 - BLEND_BIAS;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case ACCURATE_ODD:
|
||||||
|
if ((int(position.y * input_resolution.y) & 1) == 0) {
|
||||||
|
ratio = 1 - BLEND_BIAS;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ratio = BLEND_BIAS;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
frag_color = mix(scale(image, position, input_resolution, output_resolution),
|
||||||
|
scale(previous_image, position, input_resolution, output_resolution), ratio);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -42,19 +42,52 @@ static inline float4 texture(texture2d<half> texture, float2 pos)
|
|||||||
#line 1
|
#line 1
|
||||||
{filter}
|
{filter}
|
||||||
|
|
||||||
|
#define BLEND_BIAS (1.0/3.0)
|
||||||
|
|
||||||
|
enum frame_blending_mode {
|
||||||
|
DISABLED,
|
||||||
|
SIMPLE,
|
||||||
|
ACCURATE,
|
||||||
|
ACCURATE_EVEN = ACCURATE,
|
||||||
|
ACCURATE_ODD,
|
||||||
|
};
|
||||||
|
|
||||||
fragment float4 fragment_shader(rasterizer_data in [[stage_in]],
|
fragment float4 fragment_shader(rasterizer_data in [[stage_in]],
|
||||||
texture2d<half> image [[ texture(0) ]],
|
texture2d<half> image [[ texture(0) ]],
|
||||||
texture2d<half> previous_image [[ texture(1) ]],
|
texture2d<half> previous_image [[ texture(1) ]],
|
||||||
constant bool *mix_previous [[ buffer(0) ]],
|
constant enum frame_blending_mode *frame_blending_mode [[ buffer(0) ]],
|
||||||
constant float2 *output_resolution [[ buffer(1) ]])
|
constant float2 *output_resolution [[ buffer(1) ]])
|
||||||
{
|
{
|
||||||
float2 input_resolution = float2(image.get_width(), image.get_height());
|
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) {
|
float ratio;
|
||||||
return mix(scale(image, in.texcoords, input_resolution, *output_resolution),
|
switch (*frame_blending_mode) {
|
||||||
scale(previous_image, in.texcoords, input_resolution, *output_resolution), 0.5);
|
default:
|
||||||
}
|
case DISABLED:
|
||||||
return scale(image, in.texcoords, input_resolution, *output_resolution);
|
return scale(image, in.texcoords, input_resolution, *output_resolution);
|
||||||
|
case SIMPLE:
|
||||||
|
ratio = 0.5;
|
||||||
|
break;
|
||||||
|
case ACCURATE_EVEN:
|
||||||
|
if (((int)(in.texcoords.y * input_resolution.y) & 1) == 0) {
|
||||||
|
ratio = BLEND_BIAS;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ratio = 1 - BLEND_BIAS;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ACCURATE_ODD:
|
||||||
|
if (((int)(in.texcoords.y * input_resolution.y) & 1) == 0) {
|
||||||
|
ratio = 1 - BLEND_BIAS;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ratio = BLEND_BIAS;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mix(scale(image, in.texcoords, input_resolution, *output_resolution),
|
||||||
|
scale(previous_image, in.texcoords, input_resolution, *output_resolution), ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user