From 7dbd2cecd86f4e3ecbac4163dbdebd2a9b547a22 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 8 Apr 2018 08:14:07 -0700 Subject: [PATCH 01/40] Android: Avoid pread64 for > 2GB offsets. Appears to not actually work as advertised. See #10862. --- Core/FileLoaders/LocalFileLoader.cpp | 14 ++++++++++++-- Core/FileLoaders/LocalFileLoader.h | 2 ++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Core/FileLoaders/LocalFileLoader.cpp b/Core/FileLoaders/LocalFileLoader.cpp index d720bf61cf..389f47d652 100644 --- a/Core/FileLoaders/LocalFileLoader.cpp +++ b/Core/FileLoaders/LocalFileLoader.cpp @@ -37,7 +37,7 @@ LocalFileLoader::LocalFileLoader(const std::string &filename) if (fd_ == -1) { return; } -#if defined(__ANDROID__) || (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS < 64) +#if PPSSPP_PLATFORM(ANDROID) || (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS < 64) off64_t off = lseek64(fd_, 0, SEEK_END); filesize_ = off; lseek64(fd_, 0, SEEK_SET); @@ -112,7 +112,17 @@ std::string LocalFileLoader::Path() const { } size_t LocalFileLoader::ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags) { -#ifndef _WIN32 +#if PPSSPP_PLATFORM(ANDROID) + // pread64 doesn't appear to actually be 64-bit safe, though such ISOs are uncommon. See #10862. + if (absolutePos <= 0x7FFFFFFF) { + return pread64(fd_, data, bytes * count, absolutePos) / bytes; + } else { + // Since pread64 doesn't change the file offset, it should be safe to avoid the lock in the common case. + std::lock_guard guard(readLock_); + lseek64(fd_, absolutePos, SEEK_SET); + return read(fd_, data, bytes * count) / bytes; + } +#elif !defined(_WIN32) #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS < 64 return pread64(fd_, data, bytes * count, absolutePos) / bytes; #else diff --git a/Core/FileLoaders/LocalFileLoader.h b/Core/FileLoaders/LocalFileLoader.h index d5203656ea..bb5910494f 100644 --- a/Core/FileLoaders/LocalFileLoader.h +++ b/Core/FileLoaders/LocalFileLoader.h @@ -17,6 +17,7 @@ #pragma once +#include #include "Common/CommonTypes.h" #include "Core/Loaders.h" #ifdef _WIN32 @@ -42,4 +43,5 @@ private: #endif u64 filesize_; std::string filename_; + std::mutex readLock_; }; From cb83ff598b72c2d52889950279da913fad55e47f Mon Sep 17 00:00:00 2001 From: Conrad Kreyling Date: Sat, 7 Apr 2018 20:15:49 -0400 Subject: [PATCH 02/40] add updated Obj-C version of SubtleVolume --- CMakeLists.txt | 4 +- ios/SubtleVolume.h | 83 ++++++++++++++ ios/SubtleVolume.mm | 250 ++++++++++++++++++++++++++++++++++++++++++ ios/ViewController.mm | 26 ++++- 4 files changed, 360 insertions(+), 3 deletions(-) create mode 100644 ios/SubtleVolume.h create mode 100644 ios/SubtleVolume.mm diff --git a/CMakeLists.txt b/CMakeLists.txt index 99169a8f95..e26ebe2227 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -722,10 +722,12 @@ elseif(IOS) ios/PPSSPPUIApplication.mm ios/SmartKeyboardMap.cpp ios/SmartKeyboardMap.hpp + ios/SubtleVolume.h + ios/SubtleVolume.mm ios/iCade/iCadeReaderView.h ios/iCade/iCadeReaderView.m ios/iCade/iCadeState.h) - set(nativeExtraLibs ${nativeExtraLibs} "-framework Foundation -framework AudioToolbox -framework CoreGraphics -framework QuartzCore -framework UIKit -framework GLKit -framework OpenAL -framework AVFoundation") + set(nativeExtraLibs ${nativeExtraLibs} "-framework Foundation -framework MediaPlayer -framework AudioToolbox -framework CoreGraphics -framework QuartzCore -framework UIKit -framework GLKit -framework OpenAL -framework AVFoundation") if(EXISTS "${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks/GameController.framework") set(nativeExtraLibs ${nativeExtraLibs} "-weak_framework GameController") endif() diff --git a/ios/SubtleVolume.h b/ios/SubtleVolume.h new file mode 100644 index 0000000000..642e5ef1f8 --- /dev/null +++ b/ios/SubtleVolume.h @@ -0,0 +1,83 @@ +// +// SubtleVolume.h +// subtleVolumeObjC +// +// Created by iMokhles on 24/03/16. +// Copyright © 2016 iMokhles. All rights reserved. +// + +#import +#import +#import + +/** + The style of the volume indicator + - Plain: A plain bar + - RoundedLine: A plain bar with rounded corners + - Dashes: A bar divided in dashes + - Dots: A bar composed by a line of dots + */ +typedef NS_ENUM(NSInteger, SubtleVolumeStyle) { + SubtleVolumeStylePlain, + SubtleVolumeStyleRoundedLine, + SubtleVolumeStyleDashes, + SubtleVolumeStyleDots +}; + + +/** + The entry and exit animation of the volume indicator + - None: The indicator is always visible + - SlideDown: The indicator fades in/out and slides from/to the top into position + - FadeIn: The indicator fades in and out + */ +typedef NS_ENUM(NSInteger, SubtleVolumeAnimation) { + SubtleVolumeAnimationNone, + SubtleVolumeAnimationSlideDown, + SubtleVolumeAnimationFadeIn +}; + +@class SubtleVolume; +/** + Delegate protocol fo `SubtleVolume`. + Notifies the delegate when a change is about to happen (before the entry animation) + and when a change occurred (and the exit animation is complete) + */ +@protocol SubtleVolumeDelegate +/** + The volume is about to change. This is fired before performing any entry animation + - parameter subtleVolume: The current instance of `SubtleVolume` + - parameter value: The value of the volume (between 0 an 1.0) + */ +- (void)subtleVolume:(SubtleVolume *)volumeView willChange:(CGFloat)value; +/** + The volume did change. This is fired after the exit animation is done + - parameter subtleVolume: The current instance of `SubtleVolume` + - parameter value: The value of the volume (between 0 an 1.0) + */ +- (void)subtleVolume:(SubtleVolume *)volumeView didChange:(CGFloat)value; + +@end + +/** + Replace the system volume popup with a more subtle way to display the volume + when the user changes it with the volume rocker. + */ +@interface SubtleVolume : UIView +/** + The style of the volume indicator + */ +@property (nonatomic, assign) SubtleVolumeStyle style; +/** + The entry and exit animation of the indicator. The animation is triggered by the volume + If the animation is set to `SubtleVolumeAnimationNone`, the volume indicator is always visible + */ +@property (nonatomic, assign) SubtleVolumeAnimation animation; +@property (nonatomic, strong) UIColor *barBackgroundColor; +@property (nonatomic, strong) UIColor *barTintColor; +@property (nonatomic, assign) id delegate; +@property (nonatomic, assign) BOOL animatedByDefault; + +- (instancetype)initWithStyle:(SubtleVolumeStyle)style; +- (instancetype)initWithStyle:(SubtleVolumeStyle)style frame:(CGRect)frame; +@end diff --git a/ios/SubtleVolume.mm b/ios/SubtleVolume.mm new file mode 100644 index 0000000000..80de327df3 --- /dev/null +++ b/ios/SubtleVolume.mm @@ -0,0 +1,250 @@ +// +// SubtleVolume.m +// subtleVolumeObjC +// +// Created by iMokhles on 24/03/16. +// Copyright © 2016 iMokhles. All rights reserved. +// + +#import "SubtleVolume.h" + +MPVolumeView *volume = [[MPVolumeView alloc] initWithFrame:CGRectZero]; +UIView *overlay = [[UIView alloc] init]; +CGFloat volumeLevel = 0; + +@interface SubtleVolume (){ + BOOL runningShowAnimation; + BOOL showing; + BOOL runningHideAnimation; + BOOL lastAnimated; +}; + +@property (nonatomic, strong) NSTimer *timer; + +- (void)timerComplete; +- (void)doHide:(BOOL)animated; +- (void)doShow:(BOOL)animated; +- (void)stopAnimations; + +@end + + +@implementation SubtleVolume + +- (instancetype)initWithStyle:(SubtleVolumeStyle)style frame:(CGRect)frame { + + self = [super initWithFrame:frame]; + + if (self) { + self.animatedByDefault = YES; + self.style = style; + [self setup]; + } + return self; +} + +- (instancetype)initWithStyle:(SubtleVolumeStyle)style { + return [self initWithStyle:style frame:CGRectZero]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self setup]; + } + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame { + + self = [super initWithFrame:frame]; + + if (self) { + [self setup]; + } + return self; +} + +- (instancetype)init { + self = nil; + NSAssert(false, @"To init this class please use the designated initializer: initWithStyle or initWithStyle:frame:"); + return nil; +} + +- (void)setup { + @try { + [[AVAudioSession sharedInstance] setActive:YES error:nil]; + } @catch (NSException *e) { + NSLog(@"Unable to initialize AVAudioSession"); + } + + volumeLevel = [[AVAudioSession sharedInstance] outputVolume]; +// [self updateVolume:[[AVAudioSession sharedInstance] outputVolume] animated:NO]; + [[AVAudioSession sharedInstance] addObserver:self forKeyPath:@"outputVolume" options:NSKeyValueObservingOptionNew context:NULL]; + [volume setVolumeThumbImage:[[UIImage alloc] init] forState:UIControlStateNormal]; + [volume setUserInteractionEnabled:NO]; + [volume setAlpha:0.0001]; + [volume setShowsRouteButton:NO]; + self.alpha = 0.0001; + + [self addSubview:volume]; + + [self addSubview:overlay]; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + overlay.frame = self.frame; + overlay.frame = CGRectMake(0, 0, self.frame.size.width*volumeLevel, self.frame.size.height); + + self.backgroundColor = self.barBackgroundColor; + overlay.backgroundColor = self.barTintColor; + +} +- (void)updateVolume:(CGFloat)value animated:(BOOL)animated { + NSLog(@"updateVolume: value:%f animated:%@", value, animated?@"YES":@"NO"); + [self.delegate subtleVolume:self willChange:value]; + volumeLevel = value; + lastAnimated = animated; + [UIView animateWithDuration:(animated ? 0.1 : 0) animations:^{ + CGRect rectOverlayView = overlay.frame; + CGFloat overlyWidth = self.frame.size.width * volumeLevel; + rectOverlayView.size.width = overlyWidth; + overlay.frame = rectOverlayView; + }]; + + + if(self.timer) { + [self.timer invalidate]; + self.timer = nil; + } + + NSLog(@"Spinning up timer with timeInterval 2 ..."); + self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(timerComplete) userInfo:nil repeats:NO]; + + [self doShow:animated]; + + [self.delegate subtleVolume:self didChange:value]; +} + +- (void)timerComplete { + NSLog(@"timerComplete!"); + [self doHide:lastAnimated]; + self.timer = nil; +} + +- (void)doHide:(BOOL)animated { + NSLog(@"doHide:%@, runningShowAnimation: %@, runningHideAnimation: %@, showing: %@", + animated?@"YES":@"NO", + runningShowAnimation?@"YES":@"NO", + runningHideAnimation?@"YES":@"NO", + showing?@"YES":@"NO"); + + if(!showing) { + return; + } + + if(runningHideAnimation && !animated) { + [self stopAnimations]; + } + + if(runningHideAnimation) { + return; + } + + if(animated) { + runningHideAnimation = YES; + [UIView animateWithDuration:0.333 animations:^{ + switch (self.animation) { + case SubtleVolumeAnimationNone: + break; + case SubtleVolumeAnimationFadeIn: + self.alpha = 0.0001; + break; + case SubtleVolumeAnimationSlideDown: + self.alpha = 0.0001; + self.transform = CGAffineTransformMakeTranslation(0, -self.frame.size.height); + break; + default: + break; + } + } completion:^(BOOL finished) { + NSLog(@"Hide animation complete; finished? %@", finished?@"YES":@"NO"); + showing = NO; + runningHideAnimation = NO; + }]; + } else { + NSLog(@"Hide immediate complete."); + showing = NO; + self.alpha = 0.0001; + if(self.animation == SubtleVolumeAnimationSlideDown) { + self.transform = CGAffineTransformMakeTranslation(0, -self.frame.size.height); + } + } +} + +- (void)doShow:(BOOL)animated { + NSLog(@"doShow:%@, runningShowAnimation: %@, runningHideAnimation: %@, showing: %@", + animated?@"YES":@"NO", + runningShowAnimation?@"YES":@"NO", + runningHideAnimation?@"YES":@"NO", + showing?@"YES":@"NO"); + + if(showing) { + return; + } + + if(runningShowAnimation && !animated) { + [self stopAnimations]; + } + + if(runningShowAnimation) { + return; + } + + if(animated) { + runningShowAnimation = YES; + [UIView animateWithDuration:0.333 animations:^{ + switch (self.animation) { + case SubtleVolumeAnimationNone: + break; + case SubtleVolumeAnimationFadeIn: + self.alpha = 1; + break; + case SubtleVolumeAnimationSlideDown: + self.alpha = 1; + self.transform = CGAffineTransformIdentity; + break; + default: + break; + } + } completion:^(BOOL finished) { + NSLog(@"Show animation complete; finished? %@", finished?@"YES":@"NO"); + showing = YES; + runningShowAnimation = NO; + }]; + } else { + NSLog(@"Show immediate complete."); + showing = YES; + self.alpha = 1; + self.transform = CGAffineTransformIdentity; + } +} + +- (void)stopAnimations { + [self.layer removeAllAnimations]; + runningHideAnimation = NO; + runningShowAnimation = NO; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + if ([keyPath isEqual:@"outputVolume"]) { + CGFloat value = [change[@"new"] floatValue]; + [self updateVolume:value animated:self.animatedByDefault]; + } else { + return; + } + +} + +@end diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 4f7dcaad16..f57048410b 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -6,6 +6,7 @@ // #import "ViewController.h" +#import "SubtleVolume.h" #import #include @@ -81,8 +82,7 @@ static bool threadStopped = false; __unsafe_unretained ViewController* sharedViewController; static GraphicsContext *graphicsContext; -@interface ViewController () -{ +@interface ViewController () { std::map iCadeToKeyMap; } @@ -95,6 +95,12 @@ static GraphicsContext *graphicsContext; @end +@interface ViewController () { + SubtleVolume *volume; +} +@end + + @implementation ViewController -(id) init { @@ -129,6 +135,13 @@ static GraphicsContext *graphicsContext; return self; } +- (void)subtleVolume:(SubtleVolume *)volumeView willChange:(CGFloat)value { +// NSLog(@"%f alpha: %f", value, volumeView.alpha); +} +- (void)subtleVolume:(SubtleVolume *)volumeView didChange:(CGFloat)value { +// NSLog(@"END %f alpha: %f", value, volumeView.alpha); +} + - (void)viewDidLoad { [super viewDidLoad]; @@ -197,6 +210,15 @@ static GraphicsContext *graphicsContext; } #endif + volume = [[SubtleVolume alloc] initWithStyle:SubtleVolumeStylePlain frame:CGRectMake(10, 0, self.view.frame.size.width-20, 4)]; + // volume.animatedByDefault = NO; + volume.barTintColor = [UIColor whiteColor]; + volume.barBackgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.3]; + volume.animation = SubtleVolumeAnimationSlideDown; + volume.delegate = self; + [self.view addSubview:volume]; + [self.view bringSubviewToFront:volume]; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ NativeInitGraphics(graphicsContext); From 2febfa188894e59007b3eeb822ad90342dd7ea98 Mon Sep 17 00:00:00 2001 From: Conrad Kreyling Date: Sat, 7 Apr 2018 20:40:02 -0400 Subject: [PATCH 03/40] allow padding specification, remove NSLog --- ios/SubtleVolume.h | 1 + ios/SubtleVolume.mm | 36 ++++++++++++------------------------ ios/ViewController.mm | 20 ++++++++++++++------ 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/ios/SubtleVolume.h b/ios/SubtleVolume.h index 642e5ef1f8..32899f7a9e 100644 --- a/ios/SubtleVolume.h +++ b/ios/SubtleVolume.h @@ -77,6 +77,7 @@ typedef NS_ENUM(NSInteger, SubtleVolumeAnimation) { @property (nonatomic, strong) UIColor *barTintColor; @property (nonatomic, assign) id delegate; @property (nonatomic, assign) BOOL animatedByDefault; +@property (nonatomic, assign) CGFloat padding; - (instancetype)initWithStyle:(SubtleVolumeStyle)style; - (instancetype)initWithStyle:(SubtleVolumeStyle)style frame:(CGRect)frame; diff --git a/ios/SubtleVolume.mm b/ios/SubtleVolume.mm index 80de327df3..6c02939100 100644 --- a/ios/SubtleVolume.mm +++ b/ios/SubtleVolume.mm @@ -79,7 +79,6 @@ CGFloat volumeLevel = 0; } volumeLevel = [[AVAudioSession sharedInstance] outputVolume]; -// [self updateVolume:[[AVAudioSession sharedInstance] outputVolume] animated:NO]; [[AVAudioSession sharedInstance] addObserver:self forKeyPath:@"outputVolume" options:NSKeyValueObservingOptionNew context:NULL]; [volume setVolumeThumbImage:[[UIImage alloc] init] forState:UIControlStateNormal]; [volume setUserInteractionEnabled:NO]; @@ -94,15 +93,18 @@ CGFloat volumeLevel = 0; - (void)layoutSubviews { [super layoutSubviews]; - overlay.frame = self.frame; - overlay.frame = CGRectMake(0, 0, self.frame.size.width*volumeLevel, self.frame.size.height); + overlay.frame = CGRectMake( + self.padding, + self.padding, + (self.frame.size.width - (self.padding*2)) * volumeLevel, + self.frame.size.height - (self.padding*2) + ); self.backgroundColor = self.barBackgroundColor; overlay.backgroundColor = self.barTintColor; } - (void)updateVolume:(CGFloat)value animated:(BOOL)animated { - NSLog(@"updateVolume: value:%f animated:%@", value, animated?@"YES":@"NO"); [self.delegate subtleVolume:self willChange:value]; volumeLevel = value; lastAnimated = animated; @@ -119,27 +121,17 @@ CGFloat volumeLevel = 0; self.timer = nil; } - NSLog(@"Spinning up timer with timeInterval 2 ..."); self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(timerComplete) userInfo:nil repeats:NO]; - [self doShow:animated]; - [self.delegate subtleVolume:self didChange:value]; } - (void)timerComplete { - NSLog(@"timerComplete!"); [self doHide:lastAnimated]; self.timer = nil; } - (void)doHide:(BOOL)animated { - NSLog(@"doHide:%@, runningShowAnimation: %@, runningHideAnimation: %@, showing: %@", - animated?@"YES":@"NO", - runningShowAnimation?@"YES":@"NO", - runningHideAnimation?@"YES":@"NO", - showing?@"YES":@"NO"); - if(!showing) { return; } @@ -169,12 +161,10 @@ CGFloat volumeLevel = 0; break; } } completion:^(BOOL finished) { - NSLog(@"Hide animation complete; finished? %@", finished?@"YES":@"NO"); showing = NO; runningHideAnimation = NO; }]; } else { - NSLog(@"Hide immediate complete."); showing = NO; self.alpha = 0.0001; if(self.animation == SubtleVolumeAnimationSlideDown) { @@ -184,12 +174,6 @@ CGFloat volumeLevel = 0; } - (void)doShow:(BOOL)animated { - NSLog(@"doShow:%@, runningShowAnimation: %@, runningHideAnimation: %@, showing: %@", - animated?@"YES":@"NO", - runningShowAnimation?@"YES":@"NO", - runningHideAnimation?@"YES":@"NO", - showing?@"YES":@"NO"); - if(showing) { return; } @@ -203,6 +187,12 @@ CGFloat volumeLevel = 0; } if(animated) { + // set up for first run, assuming the animation has changed + // between instantiation and first showing + if(self.animation == SubtleVolumeAnimationSlideDown) { + self.transform = CGAffineTransformMakeTranslation(0, -self.frame.size.height); + } + runningShowAnimation = YES; [UIView animateWithDuration:0.333 animations:^{ switch (self.animation) { @@ -219,12 +209,10 @@ CGFloat volumeLevel = 0; break; } } completion:^(BOOL finished) { - NSLog(@"Show animation complete; finished? %@", finished?@"YES":@"NO"); showing = YES; runningShowAnimation = NO; }]; } else { - NSLog(@"Show immediate complete."); showing = YES; self.alpha = 1; self.transform = CGAffineTransformIdentity; diff --git a/ios/ViewController.mm b/ios/ViewController.mm index f57048410b..94192cc3a3 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -136,10 +136,8 @@ static GraphicsContext *graphicsContext; } - (void)subtleVolume:(SubtleVolume *)volumeView willChange:(CGFloat)value { -// NSLog(@"%f alpha: %f", value, volumeView.alpha); } - (void)subtleVolume:(SubtleVolume *)volumeView didChange:(CGFloat)value { -// NSLog(@"END %f alpha: %f", value, volumeView.alpha); } - (void)viewDidLoad { @@ -210,10 +208,20 @@ static GraphicsContext *graphicsContext; } #endif - volume = [[SubtleVolume alloc] initWithStyle:SubtleVolumeStylePlain frame:CGRectMake(10, 0, self.view.frame.size.width-20, 4)]; - // volume.animatedByDefault = NO; - volume.barTintColor = [UIColor whiteColor]; - volume.barBackgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.3]; + CGFloat margin = 0; + CGFloat height = 16; + volume = [[SubtleVolume alloc] + initWithStyle:SubtleVolumeStylePlain + frame:CGRectMake( + margin, // X + 0, // Y + self.view.frame.size.width-(margin*2), // width + height // height + )]; + + volume.padding = 7; + volume.barTintColor = [UIColor blackColor]; + volume.barBackgroundColor = [UIColor whiteColor]; volume.animation = SubtleVolumeAnimationSlideDown; volume.delegate = self; [self.view addSubview:volume]; From 3f90c97f39d2efd7a55037e938d9acb8c4f36836 Mon Sep 17 00:00:00 2001 From: Conrad Kreyling Date: Sun, 8 Apr 2018 11:30:18 -0400 Subject: [PATCH 04/40] =?UTF-8?q?fix=20=E2=80=9Ccrash=20on=20exit=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Leaving this view here causes some kinda race condition on a lock, causing a crash on exit. Simply removing the volume from the superview fixes the issue. --- ios/ViewController.mm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 94192cc3a3..cf88bdaacc 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -263,6 +263,11 @@ static GraphicsContext *graphicsContext; if (sharedViewController == nil) { return; } + + if(volume) { + [volume removeFromSuperview]; + volume = nil; + } Audio_Shutdown(); From b4e315a839dde1c4e269275ef42b6c4b9c050ca5 Mon Sep 17 00:00:00 2001 From: Conrad Kreyling Date: Sun, 8 Apr 2018 13:29:07 -0400 Subject: [PATCH 05/40] Use tabs for indentation When those Xcode defaults bite you --- ios/SubtleVolume.h | 14 +- ios/SubtleVolume.mm | 340 +++++++++++++++++++++--------------------- ios/ViewController.mm | 64 ++++---- 3 files changed, 209 insertions(+), 209 deletions(-) diff --git a/ios/SubtleVolume.h b/ios/SubtleVolume.h index 32899f7a9e..4b2ac8bb74 100644 --- a/ios/SubtleVolume.h +++ b/ios/SubtleVolume.h @@ -18,10 +18,10 @@ - Dots: A bar composed by a line of dots */ typedef NS_ENUM(NSInteger, SubtleVolumeStyle) { - SubtleVolumeStylePlain, - SubtleVolumeStyleRoundedLine, - SubtleVolumeStyleDashes, - SubtleVolumeStyleDots + SubtleVolumeStylePlain, + SubtleVolumeStyleRoundedLine, + SubtleVolumeStyleDashes, + SubtleVolumeStyleDots }; @@ -32,9 +32,9 @@ typedef NS_ENUM(NSInteger, SubtleVolumeStyle) { - FadeIn: The indicator fades in and out */ typedef NS_ENUM(NSInteger, SubtleVolumeAnimation) { - SubtleVolumeAnimationNone, - SubtleVolumeAnimationSlideDown, - SubtleVolumeAnimationFadeIn + SubtleVolumeAnimationNone, + SubtleVolumeAnimationSlideDown, + SubtleVolumeAnimationFadeIn }; @class SubtleVolume; diff --git a/ios/SubtleVolume.mm b/ios/SubtleVolume.mm index 6c02939100..57296b738e 100644 --- a/ios/SubtleVolume.mm +++ b/ios/SubtleVolume.mm @@ -13,10 +13,10 @@ UIView *overlay = [[UIView alloc] init]; CGFloat volumeLevel = 0; @interface SubtleVolume (){ - BOOL runningShowAnimation; - BOOL showing; - BOOL runningHideAnimation; - BOOL lastAnimated; + BOOL runningShowAnimation; + BOOL showing; + BOOL runningHideAnimation; + BOOL lastAnimated; }; @property (nonatomic, strong) NSTimer *timer; @@ -32,207 +32,207 @@ CGFloat volumeLevel = 0; @implementation SubtleVolume - (instancetype)initWithStyle:(SubtleVolumeStyle)style frame:(CGRect)frame { - - self = [super initWithFrame:frame]; - - if (self) { - self.animatedByDefault = YES; - self.style = style; - [self setup]; - } - return self; + + self = [super initWithFrame:frame]; + + if (self) { + self.animatedByDefault = YES; + self.style = style; + [self setup]; + } + return self; } - (instancetype)initWithStyle:(SubtleVolumeStyle)style { - return [self initWithStyle:style frame:CGRectZero]; + return [self initWithStyle:style frame:CGRectZero]; } - (instancetype)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if (self) { - [self setup]; - } - return self; + self = [super initWithCoder:aDecoder]; + if (self) { + [self setup]; + } + return self; } - (instancetype)initWithFrame:(CGRect)frame { - - self = [super initWithFrame:frame]; - - if (self) { - [self setup]; - } - return self; + + self = [super initWithFrame:frame]; + + if (self) { + [self setup]; + } + return self; } - (instancetype)init { - self = nil; - NSAssert(false, @"To init this class please use the designated initializer: initWithStyle or initWithStyle:frame:"); - return nil; + self = nil; + NSAssert(false, @"To init this class please use the designated initializer: initWithStyle or initWithStyle:frame:"); + return nil; } - (void)setup { - @try { - [[AVAudioSession sharedInstance] setActive:YES error:nil]; - } @catch (NSException *e) { - NSLog(@"Unable to initialize AVAudioSession"); - } - - volumeLevel = [[AVAudioSession sharedInstance] outputVolume]; - [[AVAudioSession sharedInstance] addObserver:self forKeyPath:@"outputVolume" options:NSKeyValueObservingOptionNew context:NULL]; - [volume setVolumeThumbImage:[[UIImage alloc] init] forState:UIControlStateNormal]; - [volume setUserInteractionEnabled:NO]; - [volume setAlpha:0.0001]; - [volume setShowsRouteButton:NO]; - self.alpha = 0.0001; - - [self addSubview:volume]; - - [self addSubview:overlay]; + @try { + [[AVAudioSession sharedInstance] setActive:YES error:nil]; + } @catch (NSException *e) { + NSLog(@"Unable to initialize AVAudioSession"); + } + + volumeLevel = [[AVAudioSession sharedInstance] outputVolume]; + [[AVAudioSession sharedInstance] addObserver:self forKeyPath:@"outputVolume" options:NSKeyValueObservingOptionNew context:NULL]; + [volume setVolumeThumbImage:[[UIImage alloc] init] forState:UIControlStateNormal]; + [volume setUserInteractionEnabled:NO]; + [volume setAlpha:0.0001]; + [volume setShowsRouteButton:NO]; + self.alpha = 0.0001; + + [self addSubview:volume]; + + [self addSubview:overlay]; } - (void)layoutSubviews { - [super layoutSubviews]; - overlay.frame = CGRectMake( - self.padding, - self.padding, - (self.frame.size.width - (self.padding*2)) * volumeLevel, - self.frame.size.height - (self.padding*2) - ); - - self.backgroundColor = self.barBackgroundColor; - overlay.backgroundColor = self.barTintColor; - + [super layoutSubviews]; + overlay.frame = CGRectMake( + self.padding, + self.padding, + (self.frame.size.width - (self.padding*2)) * volumeLevel, + self.frame.size.height - (self.padding*2) + ); + + self.backgroundColor = self.barBackgroundColor; + overlay.backgroundColor = self.barTintColor; + } - (void)updateVolume:(CGFloat)value animated:(BOOL)animated { - [self.delegate subtleVolume:self willChange:value]; - volumeLevel = value; - lastAnimated = animated; - [UIView animateWithDuration:(animated ? 0.1 : 0) animations:^{ - CGRect rectOverlayView = overlay.frame; - CGFloat overlyWidth = self.frame.size.width * volumeLevel; - rectOverlayView.size.width = overlyWidth; - overlay.frame = rectOverlayView; - }]; - - - if(self.timer) { - [self.timer invalidate]; - self.timer = nil; - } - - self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(timerComplete) userInfo:nil repeats:NO]; - [self doShow:animated]; - [self.delegate subtleVolume:self didChange:value]; + [self.delegate subtleVolume:self willChange:value]; + volumeLevel = value; + lastAnimated = animated; + [UIView animateWithDuration:(animated ? 0.1 : 0) animations:^{ + CGRect rectOverlayView = overlay.frame; + CGFloat overlyWidth = self.frame.size.width * volumeLevel; + rectOverlayView.size.width = overlyWidth; + overlay.frame = rectOverlayView; + }]; + + + if(self.timer) { + [self.timer invalidate]; + self.timer = nil; + } + + self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(timerComplete) userInfo:nil repeats:NO]; + [self doShow:animated]; + [self.delegate subtleVolume:self didChange:value]; } - (void)timerComplete { - [self doHide:lastAnimated]; - self.timer = nil; + [self doHide:lastAnimated]; + self.timer = nil; } - (void)doHide:(BOOL)animated { - if(!showing) { - return; - } - - if(runningHideAnimation && !animated) { - [self stopAnimations]; - } - - if(runningHideAnimation) { - return; - } - - if(animated) { - runningHideAnimation = YES; - [UIView animateWithDuration:0.333 animations:^{ - switch (self.animation) { - case SubtleVolumeAnimationNone: - break; - case SubtleVolumeAnimationFadeIn: - self.alpha = 0.0001; - break; - case SubtleVolumeAnimationSlideDown: - self.alpha = 0.0001; - self.transform = CGAffineTransformMakeTranslation(0, -self.frame.size.height); - break; - default: - break; - } - } completion:^(BOOL finished) { - showing = NO; - runningHideAnimation = NO; - }]; - } else { - showing = NO; - self.alpha = 0.0001; - if(self.animation == SubtleVolumeAnimationSlideDown) { - self.transform = CGAffineTransformMakeTranslation(0, -self.frame.size.height); - } - } + if(!showing) { + return; + } + + if(runningHideAnimation && !animated) { + [self stopAnimations]; + } + + if(runningHideAnimation) { + return; + } + + if(animated) { + runningHideAnimation = YES; + [UIView animateWithDuration:0.333 animations:^{ + switch (self.animation) { + case SubtleVolumeAnimationNone: + break; + case SubtleVolumeAnimationFadeIn: + self.alpha = 0.0001; + break; + case SubtleVolumeAnimationSlideDown: + self.alpha = 0.0001; + self.transform = CGAffineTransformMakeTranslation(0, -self.frame.size.height); + break; + default: + break; + } + } completion:^(BOOL finished) { + showing = NO; + runningHideAnimation = NO; + }]; + } else { + showing = NO; + self.alpha = 0.0001; + if(self.animation == SubtleVolumeAnimationSlideDown) { + self.transform = CGAffineTransformMakeTranslation(0, -self.frame.size.height); + } + } } - (void)doShow:(BOOL)animated { - if(showing) { - return; - } - - if(runningShowAnimation && !animated) { - [self stopAnimations]; - } - - if(runningShowAnimation) { - return; - } - - if(animated) { - // set up for first run, assuming the animation has changed - // between instantiation and first showing - if(self.animation == SubtleVolumeAnimationSlideDown) { - self.transform = CGAffineTransformMakeTranslation(0, -self.frame.size.height); - } - - runningShowAnimation = YES; - [UIView animateWithDuration:0.333 animations:^{ - switch (self.animation) { - case SubtleVolumeAnimationNone: - break; - case SubtleVolumeAnimationFadeIn: - self.alpha = 1; - break; - case SubtleVolumeAnimationSlideDown: - self.alpha = 1; - self.transform = CGAffineTransformIdentity; - break; - default: - break; - } - } completion:^(BOOL finished) { - showing = YES; - runningShowAnimation = NO; - }]; - } else { - showing = YES; - self.alpha = 1; - self.transform = CGAffineTransformIdentity; - } + if(showing) { + return; + } + + if(runningShowAnimation && !animated) { + [self stopAnimations]; + } + + if(runningShowAnimation) { + return; + } + + if(animated) { + // set up for first run, assuming the animation has changed + // between instantiation and first showing + if(self.animation == SubtleVolumeAnimationSlideDown) { + self.transform = CGAffineTransformMakeTranslation(0, -self.frame.size.height); + } + + runningShowAnimation = YES; + [UIView animateWithDuration:0.333 animations:^{ + switch (self.animation) { + case SubtleVolumeAnimationNone: + break; + case SubtleVolumeAnimationFadeIn: + self.alpha = 1; + break; + case SubtleVolumeAnimationSlideDown: + self.alpha = 1; + self.transform = CGAffineTransformIdentity; + break; + default: + break; + } + } completion:^(BOOL finished) { + showing = YES; + runningShowAnimation = NO; + }]; + } else { + showing = YES; + self.alpha = 1; + self.transform = CGAffineTransformIdentity; + } } - (void)stopAnimations { - [self.layer removeAllAnimations]; - runningHideAnimation = NO; - runningShowAnimation = NO; + [self.layer removeAllAnimations]; + runningHideAnimation = NO; + runningShowAnimation = NO; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if ([keyPath isEqual:@"outputVolume"]) { - CGFloat value = [change[@"new"] floatValue]; - [self updateVolume:value animated:self.animatedByDefault]; - } else { - return; - } - + if ([keyPath isEqual:@"outputVolume"]) { + CGFloat value = [change[@"new"] floatValue]; + [self updateVolume:value animated:self.animatedByDefault]; + } else { + return; + } + } @end diff --git a/ios/ViewController.mm b/ios/ViewController.mm index cf88bdaacc..8f53c065eb 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -96,7 +96,7 @@ static GraphicsContext *graphicsContext; @end @interface ViewController () { - SubtleVolume *volume; + SubtleVolume *volume; } @end @@ -208,25 +208,25 @@ static GraphicsContext *graphicsContext; } #endif - CGFloat margin = 0; - CGFloat height = 16; - volume = [[SubtleVolume alloc] - initWithStyle:SubtleVolumeStylePlain - frame:CGRectMake( - margin, // X - 0, // Y - self.view.frame.size.width-(margin*2), // width - height // height - )]; - - volume.padding = 7; - volume.barTintColor = [UIColor blackColor]; - volume.barBackgroundColor = [UIColor whiteColor]; - volume.animation = SubtleVolumeAnimationSlideDown; - volume.delegate = self; - [self.view addSubview:volume]; - [self.view bringSubviewToFront:volume]; - + CGFloat margin = 0; + CGFloat height = 16; + volume = [[SubtleVolume alloc] + initWithStyle:SubtleVolumeStylePlain + frame:CGRectMake( + margin, // X + 0, // Y + self.view.frame.size.width-(margin*2), // width + height // height + )]; + + volume.padding = 7; + volume.barTintColor = [UIColor blackColor]; + volume.barBackgroundColor = [UIColor whiteColor]; + volume.animation = SubtleVolumeAnimationSlideDown; + volume.delegate = self; + [self.view addSubview:volume]; + [self.view bringSubviewToFront:volume]; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ NativeInitGraphics(graphicsContext); @@ -263,11 +263,11 @@ static GraphicsContext *graphicsContext; if (sharedViewController == nil) { return; } - - if(volume) { - [volume removeFromSuperview]; - volume = nil; - } + + if(volume) { + [volume removeFromSuperview]; + volume = nil; + } Audio_Shutdown(); @@ -418,13 +418,13 @@ static GraphicsContext *graphicsContext; - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { - for(UITouch* touch in touches) - { - CGPoint point = [touch locationInView:self.view]; - NSDictionary* dict = [self touchDictBy:touch]; - [self touchX:point.x y:point.y code:2 pointerId:[[dict objectForKey:@"index"] intValue]]; - [self.touches removeObject:dict]; - } + for(UITouch* touch in touches) + { + CGPoint point = [touch locationInView:self.view]; + NSDictionary* dict = [self touchDictBy:touch]; + [self touchX:point.x y:point.y code:2 pointerId:[[dict objectForKey:@"index"] intValue]]; + [self.touches removeObject:dict]; + } } - (void)bindDefaultFBO From 32eb4839325b522b9c9f9eab46e71bb90048f510 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 8 Apr 2018 19:00:24 -0700 Subject: [PATCH 06/40] GLES: Defrag using deleter off render thread. Moving it to the render thread was a bit more complex because of localMemory. Fixes #10859. --- ext/native/thin3d/GLRenderManager.cpp | 32 ++++++++++++++++++++++----- ext/native/thin3d/GLRenderManager.h | 2 +- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 94b5e7b664..32936bd4be 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -13,6 +13,11 @@ #define VLOG(...) #endif +static std::thread::id renderThreadId; +static bool OnRenderThread() { + return std::this_thread::get_id() == renderThreadId; +} + // Runs on the GPU thread. void GLDeleter::Perform(GLRenderManager *renderManager) { for (auto pushBuffer : pushBuffers) { @@ -65,6 +70,7 @@ GLRenderManager::~GLRenderManager() { void GLRenderManager::ThreadStart() { queueRunner_.CreateDeviceObjects(); threadFrame_ = threadInitFrame_; + renderThreadId = std::this_thread::get_id(); bool mapBuffers = (gl_extensions.bugs & BUG_ANY_MAP_BUFFER_RANGE_SLOW) == 0; bool hasBufferStorage = gl_extensions.ARB_buffer_storage || gl_extensions.EXT_buffer_storage; @@ -574,7 +580,7 @@ GLPushBuffer::GLPushBuffer(GLRenderManager *render, GLuint target, size_t size) } GLPushBuffer::~GLPushBuffer() { - Destroy(); + Destroy(true); } void GLPushBuffer::Map() { @@ -606,6 +612,8 @@ void GLPushBuffer::Unmap() { void GLPushBuffer::Flush() { // Must be called from the render thread. + _dbg_assert_(G3D, OnRenderThread()); + buffers_[buf_].flushOffset = offset_; if (!buffers_[buf_].deviceMemory && writePtr_) { auto &info = buffers_[buf_]; @@ -647,12 +655,18 @@ bool GLPushBuffer::AddBuffer() { } // Executed on the render thread! -void GLPushBuffer::Destroy() { +void GLPushBuffer::Destroy(bool onRenderThread) { for (BufInfo &info : buffers_) { // This will automatically unmap device memory, if needed. - // NOTE: We immediately delete the buffer, don't go through the deleter, since we're on the render thread. - // render_->DeleteBuffer(info.buffer); - delete info.buffer; + // NOTE: We immediately delete the buffer, don't go through the deleter, if we're on the render thread. + if (onRenderThread) { + _dbg_assert_(G3D, OnRenderThread()); + delete info.buffer; + } else { + _dbg_assert_(G3D, !OnRenderThread()); + render_->DeleteBuffer(info.buffer); + } + FreeAlignedMemory(info.localMemory); } buffers_.clear(); @@ -684,6 +698,8 @@ void GLPushBuffer::NextBuffer(size_t minSize) { } void GLPushBuffer::Defragment() { + _dbg_assert_(G3D, std::this_thread::get_id() != renderThreadId); + if (buffers_.size() <= 1) { // Let's take this chance to jetison localMemory we don't need. for (auto &info : buffers_) { @@ -698,7 +714,7 @@ void GLPushBuffer::Defragment() { // Okay, we have more than one. Destroy them all and start over with a larger one. size_t newSize = size_ * buffers_.size(); - Destroy(); + Destroy(false); size_ = newSize; bool res = AddBuffer(); @@ -714,6 +730,8 @@ size_t GLPushBuffer::GetTotalSize() const { } void GLPushBuffer::MapDevice(GLBufferStrategy strategy) { + _dbg_assert_(G3D, OnRenderThread()); + strategy_ = strategy; if (strategy_ == GLBufferStrategy::SUBDATA) { return; @@ -746,6 +764,8 @@ void GLPushBuffer::MapDevice(GLBufferStrategy strategy) { } void GLPushBuffer::UnmapDevice() { + _dbg_assert_(G3D, OnRenderThread()); + for (auto &info : buffers_) { if (info.deviceMemory) { // TODO: Technically this can return false? diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index b54697b432..d241812441 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -288,7 +288,7 @@ private: bool AddBuffer(); void NextBuffer(size_t minSize); void Defragment(); - void Destroy(); + void Destroy(bool onRenderThread); GLRenderManager *render_; std::vector buffers_; From 6ba0a6cdfd94c8d07d5da59f46fefe3830e2120b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 8 Apr 2018 19:28:15 -0700 Subject: [PATCH 07/40] Android: End GLES renderer after emu thread join. This matches other backends, and prevents things from being added to the deleter after GLES end. Fixes #10849. --- android/jni/app-android.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 9ebb82dc0b..602bfd0025 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -551,7 +551,6 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, if (renderer_inited) { // Would be really nice if we could get something on the GL thread immediately when shutting down... ILOG("NativeApp.displayInit() restoring"); - graphicsContext->ThreadEnd(); if (useCPUThread) { EmuThreadStop(); while (graphicsContext->ThreadFrame()) { @@ -561,6 +560,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, } else { NativeShutdownGraphics(); } + graphicsContext->ThreadEnd(); graphicsContext->ShutdownFromRenderThread(); ILOG("Shut down both threads. Now let's bring it up again!"); From c0f1b7793a3ace816db57c75e2d6ba77b3552a85 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 8 Apr 2018 23:54:46 -0700 Subject: [PATCH 08/40] UI: Assert on invalid permission status. --- UI/EmuScreen.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index d9ba778f1b..3d4c791cd2 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -157,6 +157,9 @@ bool EmuScreen::bootAllowStorage(const std::string &filename) { case PERMISSION_STATUS_GRANTED: return true; } + + _assert_(false); + return false; } void EmuScreen::bootGame(const std::string &filename) { From da0173f40ca77db7109844ff234bd22a6272af0b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 9 Apr 2018 19:12:23 -0700 Subject: [PATCH 09/40] Module: Fix function hashing typo. May help #10895. --- Core/MIPS/MIPSAnalyst.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/MIPS/MIPSAnalyst.cpp b/Core/MIPS/MIPSAnalyst.cpp index 9a31123fef..569f49df5a 100644 --- a/Core/MIPS/MIPSAnalyst.cpp +++ b/Core/MIPS/MIPSAnalyst.cpp @@ -878,7 +878,7 @@ namespace MIPSAnalyst { for (auto iter = functions.begin(), end = functions.end(); iter != end; iter++) { AnalyzedFunction &f = *iter; - if (Memory::IsValidRange(f.start, f.end - f.start + 4)) { + if (!Memory::IsValidRange(f.start, f.end - f.start + 4)) { continue; } From 69309aa40086dba4e976a6f71e4ac858cb55d410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Apr 2018 11:21:56 +0200 Subject: [PATCH 10/40] Revert "Remove some remains of software skinning" This reverts commit 2d33d526b8b15f46ac0fe15ba182dae608a7cf13. --- GPU/Common/VertexDecoderCommon.cpp | 2 +- GPU/Common/VertexDecoderCommon.h | 57 ++++++++++++++++++++++++++++ GPU/D3D11/DrawEngineD3D11.cpp | 12 ++++++ GPU/Directx9/DrawEngineDX9.cpp | 10 +++++ GPU/GLES/DrawEngineGLES.cpp | 2 + GPU/Vulkan/PipelineManagerVulkan.cpp | 6 +++ 6 files changed, 88 insertions(+), 1 deletion(-) diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index 8ae7f711f2..723b3422e0 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -72,7 +72,7 @@ int DecFmtSize(u8 fmt) { } void DecVtxFormat::ComputeID() { - id = uvfmt | (c0fmt << 4) | (c1fmt << 8) | (nrmfmt << 12) | (posfmt << 16); + id = w0fmt | (w1fmt << 4) | (uvfmt << 8) | (c0fmt << 12) | (c1fmt << 16) | (nrmfmt << 20) | (posfmt << 24); } void DecVtxFormat::InitializeFromID(uint32_t id) { diff --git a/GPU/Common/VertexDecoderCommon.h b/GPU/Common/VertexDecoderCommon.h index 6acf5c48d8..9833eaac47 100644 --- a/GPU/Common/VertexDecoderCommon.h +++ b/GPU/Common/VertexDecoderCommon.h @@ -67,6 +67,8 @@ enum { int DecFmtSize(u8 fmt); struct DecVtxFormat { + u8 w0fmt; u8 w0off; // first 4 weights + u8 w1fmt; u8 w1off; // second 4 weights u8 uvfmt; u8 uvoff; u8 c0fmt; u8 c0off; // First color u8 c1fmt; u8 c1off; @@ -345,6 +347,61 @@ public: } } + void ReadWeights(float weights[8]) const { + const float *f = (const float *)(data_ + decFmt_.w0off); + const u8 *b = (const u8 *)(data_ + decFmt_.w0off); + const u16 *s = (const u16 *)(data_ + decFmt_.w0off); + switch (decFmt_.w0fmt) { + case DEC_FLOAT_1: + case DEC_FLOAT_2: + case DEC_FLOAT_3: + case DEC_FLOAT_4: + for (int i = 0; i <= decFmt_.w0fmt - DEC_FLOAT_1; i++) + weights[i] = f[i]; + break; + case DEC_U8_1: weights[0] = b[0] * (1.f / 128.f); break; + case DEC_U8_2: for (int i = 0; i < 2; i++) weights[i] = b[i] * (1.f / 128.f); break; + case DEC_U8_3: for (int i = 0; i < 3; i++) weights[i] = b[i] * (1.f / 128.f); break; + case DEC_U8_4: for (int i = 0; i < 4; i++) weights[i] = b[i] * (1.f / 128.f); break; + case DEC_U16_1: weights[0] = s[0] * (1.f / 32768.f); break; + case DEC_U16_2: for (int i = 0; i < 2; i++) weights[i] = s[i] * (1.f / 32768.f); break; + case DEC_U16_3: for (int i = 0; i < 3; i++) weights[i] = s[i] * (1.f / 32768.f); break; + case DEC_U16_4: for (int i = 0; i < 4; i++) weights[i] = s[i] * (1.f / 32768.f); break; + default: + ERROR_LOG_REPORT_ONCE(fmtw0, G3D, "Reader: Unsupported W0 Format %d", decFmt_.w0fmt); + memset(weights, 0, sizeof(float) * 8); + break; + } + + f = (const float *)(data_ + decFmt_.w1off); + b = (const u8 *)(data_ + decFmt_.w1off); + s = (const u16 *)(data_ + decFmt_.w1off); + switch (decFmt_.w1fmt) { + case 0: + // It's fine for there to be w0 weights but not w1. + break; + case DEC_FLOAT_1: + case DEC_FLOAT_2: + case DEC_FLOAT_3: + case DEC_FLOAT_4: + for (int i = 0; i <= decFmt_.w1fmt - DEC_FLOAT_1; i++) + weights[i+4] = f[i]; + break; + case DEC_U8_1: weights[4] = b[0] * (1.f / 128.f); break; + case DEC_U8_2: for (int i = 0; i < 2; i++) weights[i+4] = b[i] * (1.f / 128.f); break; + case DEC_U8_3: for (int i = 0; i < 3; i++) weights[i+4] = b[i] * (1.f / 128.f); break; + case DEC_U8_4: for (int i = 0; i < 4; i++) weights[i+4] = b[i] * (1.f / 128.f); break; + case DEC_U16_1: weights[4] = s[0] * (1.f / 32768.f); break; + case DEC_U16_2: for (int i = 0; i < 2; i++) weights[i+4] = s[i] * (1.f / 32768.f); break; + case DEC_U16_3: for (int i = 0; i < 3; i++) weights[i+4] = s[i] * (1.f / 32768.f); break; + case DEC_U16_4: for (int i = 0; i < 4; i++) weights[i+4] = s[i] * (1.f / 32768.f); break; + default: + ERROR_LOG_REPORT_ONCE(fmtw1, G3D, "Reader: Unsupported W1 Format %d", decFmt_.w1fmt); + memset(weights + 4, 0, sizeof(float) * 4); + break; + } + } + bool hasColor0() const { return decFmt_.c0fmt != 0; } bool hasColor1() const { return decFmt_.c1fmt != 0; } bool hasNormal() const { return decFmt_.nrmfmt != 0; } diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index 35b81a3d89..29273e04a2 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -204,6 +204,18 @@ ID3D11InputLayout *DrawEngineD3D11::SetupDecFmtForDraw(D3D11VertexShader *vshade D3D11_INPUT_ELEMENT_DESC VertexElements[8]; D3D11_INPUT_ELEMENT_DESC *VertexElement = &VertexElements[0]; + // Vertices Elements orders + // WEIGHT + if (decFmt.w0fmt != 0) { + VertexAttribSetup(VertexElement, decFmt.w0fmt, decFmt.w0off, "TEXCOORD", 1); + VertexElement++; + } + + if (decFmt.w1fmt != 0) { + VertexAttribSetup(VertexElement, decFmt.w1fmt, decFmt.w1off, "TEXCOORD", 2); + VertexElement++; + } + // TC if (decFmt.uvfmt != 0) { VertexAttribSetup(VertexElement, decFmt.uvfmt, decFmt.uvoff, "TEXCOORD", 0); diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index a19c1f4116..99ee183863 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -175,6 +175,16 @@ IDirect3DVertexDeclaration9 *DrawEngineDX9::SetupDecFmtForDraw(VSShader *vshader D3DVERTEXELEMENT9 *VertexElement = &VertexElements[0]; // Vertices Elements orders + // WEIGHT + if (decFmt.w0fmt != 0) { + VertexAttribSetup(VertexElement, decFmt.w0fmt, decFmt.w0off, D3DDECLUSAGE_TEXCOORD, 1); + VertexElement++; + } + + if (decFmt.w1fmt != 0) { + VertexAttribSetup(VertexElement, decFmt.w1fmt, decFmt.w1off, D3DDECLUSAGE_TEXCOORD, 2); + VertexElement++; + } // TC if (decFmt.uvfmt != 0) { diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 0acda4bd4d..9992630cdf 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -211,6 +211,8 @@ GLRInputLayout *DrawEngineGLES::SetupDecFmtForDraw(LinkedShader *program, const } std::vector entries; + VertexAttribSetup(ATTR_W1, decFmt.w0fmt, decFmt.stride, decFmt.w0off, entries); + VertexAttribSetup(ATTR_W2, decFmt.w1fmt, decFmt.stride, decFmt.w1off, entries); VertexAttribSetup(ATTR_TEXCOORD, decFmt.uvfmt, decFmt.stride, decFmt.uvoff, entries); VertexAttribSetup(ATTR_COLOR0, decFmt.c0fmt, decFmt.stride, decFmt.c0off, entries); VertexAttribSetup(ATTR_COLOR1, decFmt.c1fmt, decFmt.stride, decFmt.c1off, entries); diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index bdede4c84e..c9ee5f5c52 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -87,6 +87,12 @@ static void VertexAttribSetup(VkVertexInputAttributeDescription *attr, int fmt, // as we will only call this code when we need to create a new VkPipeline. static int SetupVertexAttribs(VkVertexInputAttributeDescription attrs[], const DecVtxFormat &decFmt) { int count = 0; + if (decFmt.w0fmt != 0) { + VertexAttribSetup(&attrs[count++], decFmt.w0fmt, decFmt.w0off, PspAttributeLocation::W1); + } + if (decFmt.w1fmt != 0) { + VertexAttribSetup(&attrs[count++], decFmt.w1fmt, decFmt.w1off, PspAttributeLocation::W2); + } if (decFmt.uvfmt != 0) { VertexAttribSetup(&attrs[count++], decFmt.uvfmt, decFmt.uvoff, PspAttributeLocation::TEXCOORD); } From 6a90b8fbb40e0d9d121153425ca27169bcfd3a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Apr 2018 11:22:58 +0200 Subject: [PATCH 11/40] Revert "Further cleanup after the removal of hardware skinning." This reverts commit f086a0915f03c32035a37bc82d2217999760ebf5. Increment the shader cache version. Restore some more. --- GPU/Common/ShaderId.cpp | 1 + GPU/Common/ShaderId.h | 3 ++- GPU/D3D11/ShaderManagerD3D11.h | 1 + GPU/Directx9/VertexShaderGeneratorDX9.cpp | 3 +++ GPU/GLES/ShaderManagerGLES.cpp | 4 +++- GPU/GLES/ShaderManagerGLES.h | 6 ++++-- GPU/Vulkan/ShaderManagerVulkan.cpp | 2 +- 7 files changed, 15 insertions(+), 5 deletions(-) diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index 7fcc34fd14..8a1667f181 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -45,6 +45,7 @@ std::string VertexShaderDesc(const ShaderID &id) { } } if (id.Bits(VS_BIT_MATERIAL_UPDATE, 3)) desc << "MatUp:" << id.Bits(VS_BIT_MATERIAL_UPDATE, 3) << " "; + if (id.Bits(VS_BIT_WEIGHT_FMTSCALE, 2)) desc << "WScale " << id.Bits(VS_BIT_WEIGHT_FMTSCALE, 2) << " "; if (id.Bit(VS_BIT_FLATSHADE)) desc << "Flat "; if (id.Bit(VS_BIT_BEZIER)) desc << "Bezier "; diff --git a/GPU/Common/ShaderId.h b/GPU/Common/ShaderId.h index 7606010b7e..8ef501a571 100644 --- a/GPU/Common/ShaderId.h +++ b/GPU/Common/ShaderId.h @@ -44,7 +44,8 @@ enum { VS_BIT_LIGHT2_ENABLE = 54, VS_BIT_LIGHT3_ENABLE = 55, VS_BIT_LIGHTING_ENABLE = 56, - // 57 - 61 are free. + VS_BIT_WEIGHT_FMTSCALE = 57, // only two bits + // 59 - 61 are free. VS_BIT_FLATSHADE = 62, // 1 bit VS_BIT_BEZIER = 63, // 1 bit // No more free diff --git a/GPU/D3D11/ShaderManagerD3D11.h b/GPU/D3D11/ShaderManagerD3D11.h index cc0da972bd..4d08c35dc4 100644 --- a/GPU/D3D11/ShaderManagerD3D11.h +++ b/GPU/D3D11/ShaderManagerD3D11.h @@ -101,6 +101,7 @@ public: // Applies dirty changes and copies the buffer. bool IsBaseDirty() { return true; } bool IsLightDirty() { return true; } + bool IsBoneDirty() { return true; } private: void Clear(); diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.cpp b/GPU/Directx9/VertexShaderGeneratorDX9.cpp index 69cdeb89aa..20472eb2fc 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.cpp +++ b/GPU/Directx9/VertexShaderGeneratorDX9.cpp @@ -89,6 +89,9 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage } } + int numBoneWeights = 0; + int boneWeightScale = id.Bits(VS_BIT_WEIGHT_FMTSCALE, 2); + if (lang == HLSL_DX9) { WRITE(p, "#pragma warning( disable : 3571 )\n"); if (isModeThrough) { diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index a585cbd174..c9e69ac2f7 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -79,6 +79,8 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, semantics.push_back({ ATTR_POSITION, "position" }); semantics.push_back({ ATTR_TEXCOORD, "texcoord" }); semantics.push_back({ ATTR_NORMAL, "normal" }); + semantics.push_back({ ATTR_W1, "w1" }); + semantics.push_back({ ATTR_W2, "w2" }); semantics.push_back({ ATTR_COLOR0, "color0" }); semantics.push_back({ ATTR_COLOR1, "color1" }); @@ -788,7 +790,7 @@ std::string ShaderManagerGLES::DebugGetShaderString(std::string id, DebugShaderT // as sometimes these features might have an effect on the ID bits. #define CACHE_HEADER_MAGIC 0x83277592 -#define CACHE_VERSION 7 +#define CACHE_VERSION 8 struct CacheHeader { uint32_t magic; uint32_t version; diff --git a/GPU/GLES/ShaderManagerGLES.h b/GPU/GLES/ShaderManagerGLES.h index 6c6efd5b3a..7f19a77e28 100644 --- a/GPU/GLES/ShaderManagerGLES.h +++ b/GPU/GLES/ShaderManagerGLES.h @@ -34,8 +34,10 @@ enum { ATTR_POSITION = 0, ATTR_TEXCOORD = 1, ATTR_NORMAL = 2, - ATTR_COLOR0 = 3, - ATTR_COLOR1 = 4, + ATTR_W1 = 3, + ATTR_W2 = 4, + ATTR_COLOR0 = 5, + ATTR_COLOR1 = 6, ATTR_COUNT, }; diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 8c36edc25a..df742c7f41 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -350,7 +350,7 @@ VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderM // instantaneous. #define CACHE_HEADER_MAGIC 0xff51f420 -#define CACHE_VERSION 9 +#define CACHE_VERSION 10 struct VulkanCacheHeader { uint32_t magic; uint32_t version; From 1c99ebcbccf6b61d07623334fd9062be52093979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Apr 2018 12:12:29 +0200 Subject: [PATCH 12/40] Fix DecFmt bug --- GPU/Common/VertexDecoderCommon.cpp | 16 ++++++++++------ GPU/GLES/ShaderManagerGLES.cpp | 2 +- GPU/Vulkan/PipelineManagerVulkan.cpp | 2 ++ GPU/Vulkan/ShaderManagerVulkan.cpp | 2 +- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index 723b3422e0..808ef71606 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -77,12 +77,16 @@ void DecVtxFormat::ComputeID() { void DecVtxFormat::InitializeFromID(uint32_t id) { this->id = id; - uvfmt = (id & 0xF); - c0fmt = ((id >> 4) & 0xF); - c1fmt = ((id >> 8) & 0xF); - nrmfmt = ((id >> 12) & 0xF); - posfmt = ((id >> 16) & 0xF); - uvoff = 0; + w0fmt = ((id) & 0xF); + w1fmt = ((id >> 4) & 0xF); + uvfmt = ((id >> 8) & 0xF); + c0fmt = ((id >> 12) & 0xF); + c1fmt = ((id >> 16) & 0xF); + nrmfmt = ((id >> 20) & 0xF); + posfmt = ((id >> 24) & 0xF); + w0off = 0; + w1off = w0off + DecFmtSize(w0fmt); + uvoff = w1off + DecFmtSize(w1fmt); c0off = uvoff + DecFmtSize(uvfmt); c1off = c0off + DecFmtSize(c0fmt); nrmoff = c1off + DecFmtSize(c1fmt); diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index c9e69ac2f7..9425589665 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -790,7 +790,7 @@ std::string ShaderManagerGLES::DebugGetShaderString(std::string id, DebugShaderT // as sometimes these features might have an effect on the ID bits. #define CACHE_HEADER_MAGIC 0x83277592 -#define CACHE_VERSION 8 +#define CACHE_VERSION 10 struct CacheHeader { uint32_t magic; uint32_t version; diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index c9ee5f5c52..376460f910 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -76,6 +76,8 @@ static const DeclTypeInfo VComp[] = { }; static void VertexAttribSetup(VkVertexInputAttributeDescription *attr, int fmt, int offset, PspAttributeLocation location) { + assert(fmt != DEC_NONE); + assert(fmt < ARRAY_SIZE(VComp)); attr->location = (uint32_t)location; attr->binding = 0; attr->format = VComp[fmt].type; diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index df742c7f41..bf2e425b7f 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -350,7 +350,7 @@ VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderM // instantaneous. #define CACHE_HEADER_MAGIC 0xff51f420 -#define CACHE_VERSION 10 +#define CACHE_VERSION 12 struct VulkanCacheHeader { uint32_t magic; uint32_t version; From 8953d7ff7312efd96ce35bf7d4f9f23460a4b427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Apr 2018 11:50:24 +0200 Subject: [PATCH 13/40] Revert "Comment fixes, reindentation." This reverts commit 6fa9fcefb2893d1e9f3250337aa4f8243b6e3b6d. --- GPU/Common/DrawEngineCommon.h | 2 +- GPU/Common/VertexDecoderCommon.cpp | 9 +- GPU/Common/VertexDecoderCommon.h | 3 + GPU/D3D11/DrawEngineD3D11.cpp | 2 +- GPU/Directx9/DrawEngineDX9.cpp | 2 +- GPU/Directx9/VertexShaderGeneratorDX9.cpp | 207 +++++++++++---------- GPU/GLES/DrawEngineGLES.cpp | 4 +- GPU/GLES/VertexShaderGeneratorGLES.cpp | 201 ++++++++++---------- GPU/Vulkan/DrawEngineVulkan.cpp | 4 +- GPU/Vulkan/VertexShaderGeneratorVulkan.cpp | 197 ++++++++++---------- 10 files changed, 325 insertions(+), 306 deletions(-) diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index d3c64d2227..45d9a185ab 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -133,7 +133,7 @@ protected: TransformedVertex *transformed = nullptr; TransformedVertex *transformedExpanded = nullptr; - // Defer all vertex decoding to a "Flush" (except when skinning) + // Defer all vertex decoding to a "Flush" (except when software skinning) struct DeferredDrawCall { void *verts; void *inds; diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index 808ef71606..6c517c5065 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -41,7 +41,7 @@ static const u8 nrmsize[4] = { 0, 3, 6, 12 }, nrmalign[4] = { 0, 1, 2, 4 }; static const u8 possize[4] = { 3, 3, 6, 12 }, posalign[4] = { 1, 1, 2, 4 }; static const u8 wtsize[4] = { 0, 1, 2, 4 }, wtalign[4] = { 0, 1, 2, 4 }; -// This array is only used when non-jitted - when jitted, the matrix +// When software skinning. This array is only used when non-jitted - when jitted, the matrix // is kept in registers. alignas(16) static float skinMatrix[12]; @@ -49,6 +49,13 @@ inline int align(int n, int align) { return (n + (align - 1)) & ~(align - 1); } +int TranslateNumBones(int bones) { + if (!bones) return 0; + if (bones < 4) return 4; + // if (bones < 8) return 8; I get drawing problems in FF:CC with this! + return bones; +} + int DecFmtSize(u8 fmt) { switch (fmt) { case DEC_NONE: return 0; diff --git a/GPU/Common/VertexDecoderCommon.h b/GPU/Common/VertexDecoderCommon.h index 9833eaac47..53173dd319 100644 --- a/GPU/Common/VertexDecoderCommon.h +++ b/GPU/Common/VertexDecoderCommon.h @@ -432,6 +432,9 @@ struct JitLookup { JitStepFunction jitFunc; }; +// Collapse to less skinning shaders to reduce shader switching, which is expensive. +int TranslateNumBones(int bones); + typedef void(*JittedVertexDecoder)(const u8 *src, u8 *dst, int count); struct VertexDecoderOptions { diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index 29273e04a2..55006baca7 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -343,7 +343,7 @@ void DrawEngineD3D11::DoFlush() { // Cannot cache vertex data with morph enabled. bool useCache = g_Config.bVertexCache && !(lastVType_ & GE_VTYPE_MORPHCOUNT_MASK); - // Also avoid caching when skinning. + // Also avoid caching when software skinning. if (lastVType_ & GE_VTYPE_WEIGHT_MASK) useCache = false; diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index 99ee183863..9330893a97 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -324,7 +324,7 @@ void DrawEngineDX9::DoFlush() { // Cannot cache vertex data with morph enabled. bool useCache = g_Config.bVertexCache && !(lastVType_ & GE_VTYPE_MORPHCOUNT_MASK); - // Also avoid caching when skinning. + // Also avoid caching when software skinning. if (lastVType_ & GE_VTYPE_WEIGHT_MASK) useCache = false; diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.cpp b/GPU/Directx9/VertexShaderGeneratorDX9.cpp index 20472eb2fc..3a9fe06465 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.cpp +++ b/GPU/Directx9/VertexShaderGeneratorDX9.cpp @@ -357,113 +357,116 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage } } } else { - // Step 1: World Transform - // Hardware tessellation - if (doSpline || doBezier) { - WRITE(p, " uint num_patches_u = %s;\n", doBezier ? "(u_spline_count_u - 1) / 3u" : "u_spline_count_u - 3"); - WRITE(p, " float2 tess_pos = In.position.xy;\n"); - WRITE(p, " int u = In.instanceId %% num_patches_u;\n"); - WRITE(p, " int v = In.instanceId / num_patches_u;\n"); - WRITE(p, " int2 patch_pos = int2(u, v);\n"); - WRITE(p, " float3 _pos[16];\n"); - WRITE(p, " float2 _tex[16];\n"); - WRITE(p, " float4 _col[16];\n"); - WRITE(p, " int idx;\n"); - WRITE(p, " int2 index;\n"); - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - WRITE(p, " idx = (%i + v%s) * u_spline_count_u + (%i + u%s);\n", i, doBezier ? " * 3" : "", j, doBezier ? " * 3" : ""); - WRITE(p, " index = int2(idx, 0);\n"); - WRITE(p, " _pos[%i] = u_tess_pos_tex.Load(index).xyz;\n", i * 4 + j); - if (doTexture && hasTexcoord && hasTexcoordTess) - WRITE(p, " _tex[%i] = u_tess_tex_tex.Load(index).xy;\n", i * 4 + j); - if (hasColor && hasColorTess) - WRITE(p, " _col[%i] = u_tess_col_tex.Load(index).rgba;\n", i * 4 + j); + // Step 1: World Transform / Skinning + if (true) { + // Hardware tessellation + if (doSpline || doBezier) { + WRITE(p, " uint num_patches_u = %s;\n", doBezier ? "(u_spline_count_u - 1) / 3u" : "u_spline_count_u - 3"); + WRITE(p, " float2 tess_pos = In.position.xy;\n"); + WRITE(p, " int u = In.instanceId %% num_patches_u;\n"); + WRITE(p, " int v = In.instanceId / num_patches_u;\n"); + WRITE(p, " int2 patch_pos = int2(u, v);\n"); + WRITE(p, " float3 _pos[16];\n"); + WRITE(p, " float2 _tex[16];\n"); + WRITE(p, " float4 _col[16];\n"); + WRITE(p, " int idx;\n"); + WRITE(p, " int2 index;\n"); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + WRITE(p, " idx = (%i + v%s) * u_spline_count_u + (%i + u%s);\n", i, doBezier ? " * 3" : "", j, doBezier ? " * 3" : ""); + WRITE(p, " index = int2(idx, 0);\n"); + WRITE(p, " _pos[%i] = u_tess_pos_tex.Load(index).xyz;\n", i * 4 + j); + if (doTexture && hasTexcoord && hasTexcoordTess) + WRITE(p, " _tex[%i] = u_tess_tex_tex.Load(index).xy;\n", i * 4 + j); + if (hasColor && hasColorTess) + WRITE(p, " _col[%i] = u_tess_col_tex.Load(index).rgba;\n", i * 4 + j); + } } - } - WRITE(p, " float2 weights[4];\n"); - if (doBezier) { - // Bernstein 3D - WRITE(p, " weights[0] = (1.0 - tess_pos) * (1.0 - tess_pos) * (1.0 - tess_pos);\n"); - WRITE(p, " weights[1] = 3.0 * tess_pos * (1.0 - tess_pos) * (1.0 - tess_pos);\n"); - WRITE(p, " weights[2] = 3.0 * tess_pos * tess_pos * (1.0 - tess_pos);\n"); - WRITE(p, " weights[3] = tess_pos * tess_pos * tess_pos;\n"); - } else if (doSpline) { - WRITE(p, " int2 spline_num_patches = int2(u_spline_count_u - 3, u_spline_count_v - 3);\n"); - WRITE(p, " int2 spline_type = int2(u_spline_type_u, u_spline_type_v);\n"); - WRITE(p, " float2 knots[6];\n"); - WRITE(p, " spline_knot(spline_num_patches, spline_type, knots, patch_pos);\n"); - WRITE(p, " spline_weight(tess_pos + patch_pos, knots, weights);\n"); - } - WRITE(p, " float3 pos = tess_sample(_pos, weights);\n"); - if (doTexture && hasTexcoord) { - if (hasTexcoordTess) - WRITE(p, " float2 tex = tess_sample(_tex, weights);\n"); - else - WRITE(p, " float2 tex = tess_pos + patch_pos;\n"); - } - if (hasColor) { - if (hasColorTess) - WRITE(p, " float4 col = tess_sample(_col, weights);\n"); - else - WRITE(p, " float4 col = u_tess_col_tex.Load(int2(0, 0)).rgba;\n"); - } - if (hasNormal) { - // Curved surface is probably always need to compute normal(not sampling from control points) + WRITE(p, " float2 weights[4];\n"); if (doBezier) { - // Bernstein derivative - WRITE(p, " float2 bernderiv[4];\n"); - WRITE(p, " bernderiv[0] = -3.0 * (tess_pos - 1.0) * (tess_pos - 1.0); \n"); - WRITE(p, " bernderiv[1] = 9.0 * tess_pos * tess_pos - 12.0 * tess_pos + 3.0; \n"); - WRITE(p, " bernderiv[2] = 3.0 * (2.0 - 3.0 * tess_pos) * tess_pos; \n"); - WRITE(p, " bernderiv[3] = 3.0 * tess_pos * tess_pos; \n"); - - WRITE(p, " float2 bernderiv_u[4];\n"); - WRITE(p, " float2 bernderiv_v[4];\n"); - WRITE(p, " for (int i = 0; i < 4; i++) {\n"); - WRITE(p, " bernderiv_u[i] = float2(bernderiv[i].x, weights[i].y);\n"); - WRITE(p, " bernderiv_v[i] = float2(weights[i].x, bernderiv[i].y);\n"); - WRITE(p, " }\n"); - - WRITE(p, " float3 du = tess_sample(_pos, bernderiv_u);\n"); - WRITE(p, " float3 dv = tess_sample(_pos, bernderiv_v);\n"); + // Bernstein 3D + WRITE(p, " weights[0] = (1.0 - tess_pos) * (1.0 - tess_pos) * (1.0 - tess_pos);\n"); + WRITE(p, " weights[1] = 3.0 * tess_pos * (1.0 - tess_pos) * (1.0 - tess_pos);\n"); + WRITE(p, " weights[2] = 3.0 * tess_pos * tess_pos * (1.0 - tess_pos);\n"); + WRITE(p, " weights[3] = tess_pos * tess_pos * tess_pos;\n"); } else if (doSpline) { - WRITE(p, " float2 tess_next_u = float2(In.normal.x, 0.0);\n"); - WRITE(p, " float2 tess_next_v = float2(0.0, In.normal.y);\n"); - // Right - WRITE(p, " float2 tess_pos_r = tess_pos + tess_next_u;\n"); - WRITE(p, " spline_weight(tess_pos_r + patch_pos, knots, weights);\n"); - WRITE(p, " float3 pos_r = tess_sample(_pos, weights);\n"); - // Left - WRITE(p, " float2 tess_pos_l = tess_pos - tess_next_u;\n"); - WRITE(p, " spline_weight(tess_pos_l + patch_pos, knots, weights);\n"); - WRITE(p, " float3 pos_l = tess_sample(_pos, weights);\n"); - // Down - WRITE(p, " float2 tess_pos_d = tess_pos + tess_next_v;\n"); - WRITE(p, " spline_weight(tess_pos_d + patch_pos, knots, weights);\n"); - WRITE(p, " float3 pos_d = tess_sample(_pos, weights);\n"); - // Up - WRITE(p, " float2 tess_pos_u = tess_pos - tess_next_v;\n"); - WRITE(p, " spline_weight(tess_pos_u + patch_pos, knots, weights);\n"); - WRITE(p, " float3 pos_u = tess_sample(_pos, weights);\n"); - - WRITE(p, " float3 du = pos_r - pos_l;\n"); - WRITE(p, " float3 dv = pos_d - pos_u;\n"); + WRITE(p, " int2 spline_num_patches = int2(u_spline_count_u - 3, u_spline_count_v - 3);\n"); + WRITE(p, " int2 spline_type = int2(u_spline_type_u, u_spline_type_v);\n"); + WRITE(p, " float2 knots[6];\n"); + WRITE(p, " spline_knot(spline_num_patches, spline_type, knots, patch_pos);\n"); + WRITE(p, " spline_weight(tess_pos + patch_pos, knots, weights);\n"); } - WRITE(p, " float3 nrm = cross(du, dv);\n"); - WRITE(p, " nrm = normalize(nrm);\n"); + WRITE(p, " float3 pos = tess_sample(_pos, weights);\n"); + if (doTexture && hasTexcoord) { + if (hasTexcoordTess) + WRITE(p, " float2 tex = tess_sample(_tex, weights);\n"); + else + WRITE(p, " float2 tex = tess_pos + patch_pos;\n"); + } + if (hasColor) { + if (hasColorTess) + WRITE(p, " float4 col = tess_sample(_col, weights);\n"); + else + WRITE(p, " float4 col = u_tess_col_tex.Load(int2(0, 0)).rgba;\n"); + } + if (hasNormal) { + // Curved surface is probably always need to compute normal(not sampling from control points) + if (doBezier) { + // Bernstein derivative + WRITE(p, " float2 bernderiv[4];\n"); + WRITE(p, " bernderiv[0] = -3.0 * (tess_pos - 1.0) * (tess_pos - 1.0); \n"); + WRITE(p, " bernderiv[1] = 9.0 * tess_pos * tess_pos - 12.0 * tess_pos + 3.0; \n"); + WRITE(p, " bernderiv[2] = 3.0 * (2.0 - 3.0 * tess_pos) * tess_pos; \n"); + WRITE(p, " bernderiv[3] = 3.0 * tess_pos * tess_pos; \n"); + + WRITE(p, " float2 bernderiv_u[4];\n"); + WRITE(p, " float2 bernderiv_v[4];\n"); + WRITE(p, " for (int i = 0; i < 4; i++) {\n"); + WRITE(p, " bernderiv_u[i] = float2(bernderiv[i].x, weights[i].y);\n"); + WRITE(p, " bernderiv_v[i] = float2(weights[i].x, bernderiv[i].y);\n"); + WRITE(p, " }\n"); + + WRITE(p, " float3 du = tess_sample(_pos, bernderiv_u);\n"); + WRITE(p, " float3 dv = tess_sample(_pos, bernderiv_v);\n"); + } else if (doSpline) { + WRITE(p, " float2 tess_next_u = float2(In.normal.x, 0.0);\n"); + WRITE(p, " float2 tess_next_v = float2(0.0, In.normal.y);\n"); + // Right + WRITE(p, " float2 tess_pos_r = tess_pos + tess_next_u;\n"); + WRITE(p, " spline_weight(tess_pos_r + patch_pos, knots, weights);\n"); + WRITE(p, " float3 pos_r = tess_sample(_pos, weights);\n"); + // Left + WRITE(p, " float2 tess_pos_l = tess_pos - tess_next_u;\n"); + WRITE(p, " spline_weight(tess_pos_l + patch_pos, knots, weights);\n"); + WRITE(p, " float3 pos_l = tess_sample(_pos, weights);\n"); + // Down + WRITE(p, " float2 tess_pos_d = tess_pos + tess_next_v;\n"); + WRITE(p, " spline_weight(tess_pos_d + patch_pos, knots, weights);\n"); + WRITE(p, " float3 pos_d = tess_sample(_pos, weights);\n"); + // Up + WRITE(p, " float2 tess_pos_u = tess_pos - tess_next_v;\n"); + WRITE(p, " spline_weight(tess_pos_u + patch_pos, knots, weights);\n"); + WRITE(p, " float3 pos_u = tess_sample(_pos, weights);\n"); + + WRITE(p, " float3 du = pos_r - pos_l;\n"); + WRITE(p, " float3 dv = pos_d - pos_u;\n"); + } + WRITE(p, " float3 nrm = cross(du, dv);\n"); + WRITE(p, " nrm = normalize(nrm);\n"); + } + WRITE(p, " float3 worldpos = mul(float4(pos.xyz, 1.0), u_world);\n"); + if (hasNormal) + WRITE(p, " float3 worldnormal = normalize(mul(float4(%snrm, 0.0), u_world));\n", flipNormalTess ? "-" : ""); + else + WRITE(p, " float3 worldnormal = float3(0.0, 0.0, 1.0);\n"); + } else { + // No skinning, just standard T&L. + WRITE(p, " float3 worldpos = mul(float4(In.position.xyz, 1.0), u_world);\n"); + if (hasNormal) + WRITE(p, " float3 worldnormal = normalize(mul(float4(%sIn.normal, 0.0), u_world));\n", flipNormal ? "-" : ""); + else + WRITE(p, " float3 worldnormal = float3(0.0, 0.0, 1.0);\n"); } - WRITE(p, " float3 worldpos = mul(float4(pos.xyz, 1.0), u_world);\n"); - if (hasNormal) - WRITE(p, " float3 worldnormal = normalize(mul(float4(%snrm, 0.0), u_world));\n", flipNormalTess ? "-" : ""); - else - WRITE(p, " float3 worldnormal = float3(0.0, 0.0, 1.0);\n"); - } else { - WRITE(p, " float3 worldpos = mul(float4(In.position.xyz, 1.0), u_world);\n"); - if (hasNormal) - WRITE(p, " float3 worldnormal = normalize(mul(float4(%sIn.normal, 0.0), u_world));\n", flipNormal ? "-" : ""); - else - WRITE(p, " float3 worldnormal = float3(0.0, 0.0, 1.0);\n"); } WRITE(p, " float4 viewPos = float4(mul(float4(worldpos, 1.0), u_view), 1.0);\n"); diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 9992630cdf..c304d5d236 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -324,7 +324,7 @@ void DrawEngineGLES::DoFlush() { // Cannot cache vertex data with morph enabled. bool useCache = g_Config.bVertexCache && !(lastVType_ & GE_VTYPE_MORPHCOUNT_MASK); - // Also avoid caching when skinning. + // Also avoid caching when software skinning. if (lastVType_ & GE_VTYPE_WEIGHT_MASK) useCache = false; @@ -470,7 +470,7 @@ void DrawEngineGLES::DoFlush() { vai->lastFrame = gpuStats.numFlips; } else { if (lastVType_ & GE_VTYPE_WEIGHT_MASK) { - // If skinning, we've already predecoded into "decoded". So push that content. + // If software skinning, we've already predecoded into "decoded". So push that content. size_t size = decodedVerts_ * dec_->GetDecVtxFmt().stride; u8 *dest = (u8 *)frameData.pushVertex->Push(size, &vertexBufferOffset, &vertexBuffer); memcpy(dest, decoded, size); diff --git a/GPU/GLES/VertexShaderGeneratorGLES.cpp b/GPU/GLES/VertexShaderGeneratorGLES.cpp index a528ca9233..97e7ab3ef1 100644 --- a/GPU/GLES/VertexShaderGeneratorGLES.cpp +++ b/GPU/GLES/VertexShaderGeneratorGLES.cpp @@ -435,111 +435,114 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask, } } } else { - // Step 1: World Transform - // Hardware tessellation - if (doBezier || doSpline) { - WRITE(p, " vec3 _pos[16];\n"); - WRITE(p, " vec2 _tex[16];\n"); - WRITE(p, " vec4 _col[16];\n"); - WRITE(p, " int num_patches_u = %s;\n", doBezier ? "(u_spline_count_u - 1) / 3" : "u_spline_count_u - 3"); - WRITE(p, " int u = int(mod(float(gl_InstanceID), float(num_patches_u)));\n"); - WRITE(p, " int v = gl_InstanceID / num_patches_u;\n"); - WRITE(p, " ivec2 patch_pos = ivec2(u, v);\n"); - WRITE(p, " for (int i = 0; i < 4; i++) {\n"); - WRITE(p, " for (int j = 0; j < 4; j++) {\n"); - WRITE(p, " int index = (i + v%s) * u_spline_count_u + (j + u%s);\n", doBezier ? " * 3" : "", doBezier ? " * 3" : ""); - WRITE(p, " _pos[i * 4 + j] = %s(u_tess_pos_tex, ivec2(index, 0), 0).xyz;\n", texelFetch); - if (doTexture && hasTexcoord && hasTexcoordTess) - WRITE(p, " _tex[i * 4 + j] = %s(u_tess_tex_tex, ivec2(index, 0), 0).xy;\n", texelFetch); - if (hasColor && hasColorTess) - WRITE(p, " _col[i * 4 + j] = %s(u_tess_col_tex, ivec2(index, 0), 0).rgba;\n", texelFetch); - WRITE(p, " }\n"); - WRITE(p, " }\n"); - WRITE(p, " vec2 tess_pos = position.xy;\n"); - WRITE(p, " vec2 weights[4];\n"); - if (doBezier) { - // Bernstein 3D - WRITE(p, " weights[0] = (1.0 - tess_pos) * (1.0 - tess_pos) * (1.0 - tess_pos);\n"); - WRITE(p, " weights[1] = 3.0 * tess_pos * (1.0 - tess_pos) * (1.0 - tess_pos);\n"); - WRITE(p, " weights[2] = 3.0 * tess_pos * tess_pos * (1.0 - tess_pos);\n"); - WRITE(p, " weights[3] = tess_pos * tess_pos * tess_pos;\n"); - } else { // Spline - WRITE(p, " ivec2 spline_num_patches = ivec2(u_spline_count_u - 3, u_spline_count_v - 3);\n"); - WRITE(p, " ivec2 spline_type = ivec2(u_spline_type_u, u_spline_type_v);\n"); - WRITE(p, " vec2 knots[6];\n"); - WRITE(p, " spline_knot(spline_num_patches, spline_type, knots, patch_pos);\n"); - WRITE(p, " spline_weight(tess_pos + vec2(patch_pos), knots, weights);\n"); - } - WRITE(p, " vec3 pos = tess_sample(_pos, weights);\n"); - if (doTexture && hasTexcoord) { - if (hasTexcoordTess) - WRITE(p, " vec2 tex = tess_sample(_tex, weights);\n"); - else - WRITE(p, " vec2 tex = tess_pos + vec2(patch_pos);\n"); - } - if (hasColor) { - if (hasColorTess) - WRITE(p, " vec4 col = tess_sample(_col, weights);\n"); - else - WRITE(p, " vec4 col = %s(u_tess_col_tex, ivec2(0, 0), 0).rgba;\n", texelFetch); - } - if (hasNormal) { - // Curved surface is probably always need to compute normal(not sampling from control points) + // Step 1: World Transform / Skinning + if (true) { + // Hardware tessellation + if (doBezier || doSpline) { + WRITE(p, " vec3 _pos[16];\n"); + WRITE(p, " vec2 _tex[16];\n"); + WRITE(p, " vec4 _col[16];\n"); + WRITE(p, " int num_patches_u = %s;\n", doBezier ? "(u_spline_count_u - 1) / 3" : "u_spline_count_u - 3"); + WRITE(p, " int u = int(mod(float(gl_InstanceID), float(num_patches_u)));\n"); + WRITE(p, " int v = gl_InstanceID / num_patches_u;\n"); + WRITE(p, " ivec2 patch_pos = ivec2(u, v);\n"); + WRITE(p, " for (int i = 0; i < 4; i++) {\n"); + WRITE(p, " for (int j = 0; j < 4; j++) {\n"); + WRITE(p, " int index = (i + v%s) * u_spline_count_u + (j + u%s);\n", doBezier ? " * 3" : "", doBezier ? " * 3" : ""); + WRITE(p, " _pos[i * 4 + j] = %s(u_tess_pos_tex, ivec2(index, 0), 0).xyz;\n", texelFetch); + if (doTexture && hasTexcoord && hasTexcoordTess) + WRITE(p, " _tex[i * 4 + j] = %s(u_tess_tex_tex, ivec2(index, 0), 0).xy;\n", texelFetch); + if (hasColor && hasColorTess) + WRITE(p, " _col[i * 4 + j] = %s(u_tess_col_tex, ivec2(index, 0), 0).rgba;\n", texelFetch); + WRITE(p, " }\n"); + WRITE(p, " }\n"); + WRITE(p, " vec2 tess_pos = position.xy;\n"); + WRITE(p, " vec2 weights[4];\n"); if (doBezier) { - // Bernstein derivative - WRITE(p, " vec2 bernderiv[4];\n"); - WRITE(p, " bernderiv[0] = -3.0 * (tess_pos - 1.0) * (tess_pos - 1.0); \n"); - WRITE(p, " bernderiv[1] = 9.0 * tess_pos * tess_pos - 12.0 * tess_pos + 3.0; \n"); - WRITE(p, " bernderiv[2] = 3.0 * (2.0 - 3.0 * tess_pos) * tess_pos; \n"); - WRITE(p, " bernderiv[3] = 3.0 * tess_pos * tess_pos; \n"); - - WRITE(p, " vec2 bernderiv_u[4];\n"); - WRITE(p, " vec2 bernderiv_v[4];\n"); - WRITE(p, " for (int i = 0; i < 4; i++) {\n"); - WRITE(p, " bernderiv_u[i] = vec2(bernderiv[i].x, weights[i].y);\n"); - WRITE(p, " bernderiv_v[i] = vec2(weights[i].x, bernderiv[i].y);\n"); - WRITE(p, " }\n"); - - WRITE(p, " vec3 du = tess_sample(_pos, bernderiv_u);\n"); - WRITE(p, " vec3 dv = tess_sample(_pos, bernderiv_v);\n"); + // Bernstein 3D + WRITE(p, " weights[0] = (1.0 - tess_pos) * (1.0 - tess_pos) * (1.0 - tess_pos);\n"); + WRITE(p, " weights[1] = 3.0 * tess_pos * (1.0 - tess_pos) * (1.0 - tess_pos);\n"); + WRITE(p, " weights[2] = 3.0 * tess_pos * tess_pos * (1.0 - tess_pos);\n"); + WRITE(p, " weights[3] = tess_pos * tess_pos * tess_pos;\n"); } else { // Spline - WRITE(p, " vec2 tess_next_u = vec2(normal.x, 0.0);\n"); - WRITE(p, " vec2 tess_next_v = vec2(0.0, normal.y);\n"); - // Right - WRITE(p, " vec2 tess_pos_r = tess_pos + tess_next_u;\n"); - WRITE(p, " spline_weight(tess_pos_r + vec2(patch_pos), knots, weights);\n"); - WRITE(p, " vec3 pos_r = tess_sample(_pos, weights);\n"); - // Left - WRITE(p, " vec2 tess_pos_l = tess_pos - tess_next_u;\n"); - WRITE(p, " spline_weight(tess_pos_l + vec2(patch_pos), knots, weights);\n"); - WRITE(p, " vec3 pos_l = tess_sample(_pos, weights);\n"); - // Down - WRITE(p, " vec2 tess_pos_d = tess_pos + tess_next_v;\n"); - WRITE(p, " spline_weight(tess_pos_d + vec2(patch_pos), knots, weights);\n"); - WRITE(p, " vec3 pos_d = tess_sample(_pos, weights);\n"); - // Up - WRITE(p, " vec2 tess_pos_u = tess_pos - tess_next_v;\n"); - WRITE(p, " spline_weight(tess_pos_u + vec2(patch_pos), knots, weights);\n"); - WRITE(p, " vec3 pos_u = tess_sample(_pos, weights);\n"); + WRITE(p, " ivec2 spline_num_patches = ivec2(u_spline_count_u - 3, u_spline_count_v - 3);\n"); + WRITE(p, " ivec2 spline_type = ivec2(u_spline_type_u, u_spline_type_v);\n"); + WRITE(p, " vec2 knots[6];\n"); + WRITE(p, " spline_knot(spline_num_patches, spline_type, knots, patch_pos);\n"); + WRITE(p, " spline_weight(tess_pos + vec2(patch_pos), knots, weights);\n"); + } + WRITE(p, " vec3 pos = tess_sample(_pos, weights);\n"); + if (doTexture && hasTexcoord) { + if (hasTexcoordTess) + WRITE(p, " vec2 tex = tess_sample(_tex, weights);\n"); + else + WRITE(p, " vec2 tex = tess_pos + vec2(patch_pos);\n"); + } + if (hasColor) { + if (hasColorTess) + WRITE(p, " vec4 col = tess_sample(_col, weights);\n"); + else + WRITE(p, " vec4 col = %s(u_tess_col_tex, ivec2(0, 0), 0).rgba;\n", texelFetch); + } + if (hasNormal) { + // Curved surface is probably always need to compute normal(not sampling from control points) + if (doBezier) { + // Bernstein derivative + WRITE(p, " vec2 bernderiv[4];\n"); + WRITE(p, " bernderiv[0] = -3.0 * (tess_pos - 1.0) * (tess_pos - 1.0); \n"); + WRITE(p, " bernderiv[1] = 9.0 * tess_pos * tess_pos - 12.0 * tess_pos + 3.0; \n"); + WRITE(p, " bernderiv[2] = 3.0 * (2.0 - 3.0 * tess_pos) * tess_pos; \n"); + WRITE(p, " bernderiv[3] = 3.0 * tess_pos * tess_pos; \n"); - WRITE(p, " vec3 du = pos_r - pos_l;\n"); - WRITE(p, " vec3 dv = pos_d - pos_u;\n"); + WRITE(p, " vec2 bernderiv_u[4];\n"); + WRITE(p, " vec2 bernderiv_v[4];\n"); + WRITE(p, " for (int i = 0; i < 4; i++) {\n"); + WRITE(p, " bernderiv_u[i] = vec2(bernderiv[i].x, weights[i].y);\n"); + WRITE(p, " bernderiv_v[i] = vec2(weights[i].x, bernderiv[i].y);\n"); + WRITE(p, " }\n"); + + WRITE(p, " vec3 du = tess_sample(_pos, bernderiv_u);\n"); + WRITE(p, " vec3 dv = tess_sample(_pos, bernderiv_v);\n"); + } else { // Spline + WRITE(p, " vec2 tess_next_u = vec2(normal.x, 0.0);\n"); + WRITE(p, " vec2 tess_next_v = vec2(0.0, normal.y);\n"); + // Right + WRITE(p, " vec2 tess_pos_r = tess_pos + tess_next_u;\n"); + WRITE(p, " spline_weight(tess_pos_r + vec2(patch_pos), knots, weights);\n"); + WRITE(p, " vec3 pos_r = tess_sample(_pos, weights);\n"); + // Left + WRITE(p, " vec2 tess_pos_l = tess_pos - tess_next_u;\n"); + WRITE(p, " spline_weight(tess_pos_l + vec2(patch_pos), knots, weights);\n"); + WRITE(p, " vec3 pos_l = tess_sample(_pos, weights);\n"); + // Down + WRITE(p, " vec2 tess_pos_d = tess_pos + tess_next_v;\n"); + WRITE(p, " spline_weight(tess_pos_d + vec2(patch_pos), knots, weights);\n"); + WRITE(p, " vec3 pos_d = tess_sample(_pos, weights);\n"); + // Up + WRITE(p, " vec2 tess_pos_u = tess_pos - tess_next_v;\n"); + WRITE(p, " spline_weight(tess_pos_u + vec2(patch_pos), knots, weights);\n"); + WRITE(p, " vec3 pos_u = tess_sample(_pos, weights);\n"); + + WRITE(p, " vec3 du = pos_r - pos_l;\n"); + WRITE(p, " vec3 dv = pos_d - pos_u;\n"); + } + WRITE(p, " vec3 nrm = cross(du, dv);\n"); + WRITE(p, " nrm = normalize(nrm);\n"); + } + WRITE(p, " vec3 worldpos = (u_world * vec4(pos.xyz, 1.0)).xyz;\n"); + if (hasNormal) { + WRITE(p, " mediump vec3 worldnormal = normalize((u_world * vec4(%snrm, 0.0)).xyz);\n", flipNormalTess ? "-" : ""); + } else { + WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n"); } - WRITE(p, " vec3 nrm = cross(du, dv);\n"); - WRITE(p, " nrm = normalize(nrm);\n"); - } - WRITE(p, " vec3 worldpos = (u_world * vec4(pos.xyz, 1.0)).xyz;\n"); - if (hasNormal) { - WRITE(p, " mediump vec3 worldnormal = normalize((u_world * vec4(%snrm, 0.0)).xyz);\n", flipNormalTess ? "-" : ""); } else { - WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n"); + // No skinning, just standard T&L. + WRITE(p, " vec3 worldpos = (u_world * vec4(position.xyz, 1.0)).xyz;\n"); + if (hasNormal) + WRITE(p, " mediump vec3 worldnormal = normalize((u_world * vec4(%snormal, 0.0)).xyz);\n", flipNormal ? "-" : ""); + else + WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n"); } - } else { - WRITE(p, " vec3 worldpos = (u_world * vec4(position.xyz, 1.0)).xyz;\n"); - if (hasNormal) - WRITE(p, " mediump vec3 worldnormal = normalize((u_world * vec4(%snormal, 0.0)).xyz);\n", flipNormal ? "-" : ""); - else - WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n"); } WRITE(p, " vec4 viewPos = u_view * vec4(worldpos, 1.0);\n"); diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index fbbb05261f..b261c1cc4c 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -585,7 +585,7 @@ void DrawEngineVulkan::DoFlush() { // Cannot cache vertex data with morph enabled. bool useCache = g_Config.bVertexCache && !(lastVType_ & GE_VTYPE_MORPHCOUNT_MASK); - // Also avoid caching when skinning. + // Also avoid caching when software skinning. VkBuffer vbuf = VK_NULL_HANDLE; VkBuffer ibuf = VK_NULL_HANDLE; if (lastVType_ & GE_VTYPE_WEIGHT_MASK) { @@ -731,7 +731,7 @@ void DrawEngineVulkan::DoFlush() { } } else { if (lastVType_ & GE_VTYPE_WEIGHT_MASK) { - // If skinning, we've already predecoded into "decoded". So push that content. + // If software skinning, we've already predecoded into "decoded". So push that content. VkDeviceSize size = decodedVerts_ * dec_->GetDecVtxFmt().stride; u8 *dest = (u8 *)frame->pushVertex->Push(size, &vbOffset, &vbuf); memcpy(dest, decoded, size); diff --git a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp index eaeb8b9180..0b39ef5085 100644 --- a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp @@ -307,109 +307,112 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) { } } else { // Step 1: World Transform / Skinning - if (doBezier || doSpline) { - WRITE(p, " vec3 _pos[16];\n"); - WRITE(p, " vec2 _tex[16];\n"); - WRITE(p, " vec4 _col[16];\n"); - WRITE(p, " int num_patches_u = %s;\n", doBezier ? "(base.spline_count_u - 1) / 3" : "base.spline_count_u - 3"); - WRITE(p, " int u = int(mod(gl_InstanceIndex, num_patches_u));\n"); - WRITE(p, " int v = gl_InstanceIndex / num_patches_u;\n"); - WRITE(p, " ivec2 patch_pos = ivec2(u, v);\n"); - WRITE(p, " for (int i = 0; i < 4; i++) {\n"); - WRITE(p, " for (int j = 0; j < 4; j++) {\n"); - WRITE(p, " int idx = (i + v%s) * base.spline_count_u + (j + u%s);\n", doBezier ? " * 3" : "", doBezier ? " * 3" : ""); - WRITE(p, " _pos[i * 4 + j] = tess_data.data[idx].pos.xyz;\n"); - if (doTexture && hasTexcoord && hasTexcoordTess) - WRITE(p, " _tex[i * 4 + j] = tess_data.data[idx].uv.xy;\n"); - if (hasColor && hasColorTess) - WRITE(p, " _col[i * 4 + j] = tess_data.data[idx].color;\n"); - WRITE(p, " }\n"); - WRITE(p, " }\n"); - WRITE(p, " vec2 tess_pos = position.xy;\n"); - WRITE(p, " vec2 weights[4];\n"); - if (doBezier) { - // Bernstein 3D - WRITE(p, " weights[0] = (1 - tess_pos) * (1 - tess_pos) * (1 - tess_pos);\n"); - WRITE(p, " weights[1] = 3 * tess_pos * (1 - tess_pos) * (1 - tess_pos);\n"); - WRITE(p, " weights[2] = 3 * tess_pos * tess_pos * (1 - tess_pos);\n"); - WRITE(p, " weights[3] = tess_pos * tess_pos * tess_pos;\n"); - } else { // Spline - WRITE(p, " ivec2 spline_num_patches = ivec2(base.spline_count_u - 3, base.spline_count_v - 3);\n"); - WRITE(p, " ivec2 spline_type = ivec2(base.spline_type_u, base.spline_type_v);\n"); - WRITE(p, " vec2 knots[6];\n"); - WRITE(p, " spline_knot(spline_num_patches, spline_type, knots, patch_pos);\n"); - WRITE(p, " spline_weight(tess_pos + patch_pos, knots, weights);\n"); - } - WRITE(p, " vec3 pos = tess_sample(_pos, weights);\n"); - if (doTexture && hasTexcoord) { - if (hasTexcoordTess) - WRITE(p, " vec2 tex = tess_sample(_tex, weights);\n"); - else - WRITE(p, " vec2 tex = tess_pos + patch_pos;\n"); - } - if (hasColor) { - if (hasColorTess) - WRITE(p, " vec4 col = tess_sample(_col, weights);\n"); - else - WRITE(p, " vec4 col = tess_data.data[0].color;\n"); - } - if (hasNormal) { - // Curved surface is probably always need to compute normal(not sampling from control points) + if (true) { + if (doBezier || doSpline) { + WRITE(p, " vec3 _pos[16];\n"); + WRITE(p, " vec2 _tex[16];\n"); + WRITE(p, " vec4 _col[16];\n"); + WRITE(p, " int num_patches_u = %s;\n", doBezier ? "(base.spline_count_u - 1) / 3" : "base.spline_count_u - 3"); + WRITE(p, " int u = int(mod(gl_InstanceIndex, num_patches_u));\n"); + WRITE(p, " int v = gl_InstanceIndex / num_patches_u;\n"); + WRITE(p, " ivec2 patch_pos = ivec2(u, v);\n"); + WRITE(p, " for (int i = 0; i < 4; i++) {\n"); + WRITE(p, " for (int j = 0; j < 4; j++) {\n"); + WRITE(p, " int idx = (i + v%s) * base.spline_count_u + (j + u%s);\n", doBezier ? " * 3" : "", doBezier ? " * 3" : ""); + WRITE(p, " _pos[i * 4 + j] = tess_data.data[idx].pos.xyz;\n"); + if (doTexture && hasTexcoord && hasTexcoordTess) + WRITE(p, " _tex[i * 4 + j] = tess_data.data[idx].uv.xy;\n"); + if (hasColor && hasColorTess) + WRITE(p, " _col[i * 4 + j] = tess_data.data[idx].color;\n"); + WRITE(p, " }\n"); + WRITE(p, " }\n"); + WRITE(p, " vec2 tess_pos = position.xy;\n"); + WRITE(p, " vec2 weights[4];\n"); if (doBezier) { - // Bernstein derivative - WRITE(p, " vec2 bernderiv[4];\n"); - WRITE(p, " bernderiv[0] = -3 * (tess_pos - 1) * (tess_pos - 1); \n"); - WRITE(p, " bernderiv[1] = 9 * tess_pos * tess_pos - 12 * tess_pos + 3; \n"); - WRITE(p, " bernderiv[2] = 3 * (2 - 3 * tess_pos) * tess_pos; \n"); - WRITE(p, " bernderiv[3] = 3 * tess_pos * tess_pos; \n"); - - WRITE(p, " vec2 bernderiv_u[4];\n"); - WRITE(p, " vec2 bernderiv_v[4];\n"); - WRITE(p, " for (int i = 0; i < 4; i++) {\n"); - WRITE(p, " bernderiv_u[i] = vec2(bernderiv[i].x, weights[i].y);\n"); - WRITE(p, " bernderiv_v[i] = vec2(weights[i].x, bernderiv[i].y);\n"); - WRITE(p, " }\n"); - - WRITE(p, " vec3 du = tess_sample(_pos, bernderiv_u);\n"); - WRITE(p, " vec3 dv = tess_sample(_pos, bernderiv_v);\n"); + // Bernstein 3D + WRITE(p, " weights[0] = (1 - tess_pos) * (1 - tess_pos) * (1 - tess_pos);\n"); + WRITE(p, " weights[1] = 3 * tess_pos * (1 - tess_pos) * (1 - tess_pos);\n"); + WRITE(p, " weights[2] = 3 * tess_pos * tess_pos * (1 - tess_pos);\n"); + WRITE(p, " weights[3] = tess_pos * tess_pos * tess_pos;\n"); } else { // Spline - WRITE(p, " vec2 tess_next_u = vec2(normal.x, 0);\n"); - WRITE(p, " vec2 tess_next_v = vec2(0, normal.y);\n"); - // Right - WRITE(p, " vec2 tess_pos_r = tess_pos + tess_next_u;\n"); - WRITE(p, " spline_weight(tess_pos_r + patch_pos, knots, weights);\n"); - WRITE(p, " vec3 pos_r = tess_sample(_pos, weights);\n"); - // Left - WRITE(p, " vec2 tess_pos_l = tess_pos - tess_next_u;\n"); - WRITE(p, " spline_weight(tess_pos_l + patch_pos, knots, weights);\n"); - WRITE(p, " vec3 pos_l = tess_sample(_pos, weights);\n"); - // Down - WRITE(p, " vec2 tess_pos_d = tess_pos + tess_next_v;\n"); - WRITE(p, " spline_weight(tess_pos_d + patch_pos, knots, weights);\n"); - WRITE(p, " vec3 pos_d = tess_sample(_pos, weights);\n"); - // Up - WRITE(p, " vec2 tess_pos_u = tess_pos - tess_next_v;\n"); - WRITE(p, " spline_weight(tess_pos_u + patch_pos, knots, weights);\n"); - WRITE(p, " vec3 pos_u = tess_sample(_pos, weights);\n"); + WRITE(p, " ivec2 spline_num_patches = ivec2(base.spline_count_u - 3, base.spline_count_v - 3);\n"); + WRITE(p, " ivec2 spline_type = ivec2(base.spline_type_u, base.spline_type_v);\n"); + WRITE(p, " vec2 knots[6];\n"); + WRITE(p, " spline_knot(spline_num_patches, spline_type, knots, patch_pos);\n"); + WRITE(p, " spline_weight(tess_pos + patch_pos, knots, weights);\n"); + } + WRITE(p, " vec3 pos = tess_sample(_pos, weights);\n"); + if (doTexture && hasTexcoord) { + if (hasTexcoordTess) + WRITE(p, " vec2 tex = tess_sample(_tex, weights);\n"); + else + WRITE(p, " vec2 tex = tess_pos + patch_pos;\n"); + } + if (hasColor) { + if (hasColorTess) + WRITE(p, " vec4 col = tess_sample(_col, weights);\n"); + else + WRITE(p, " vec4 col = tess_data.data[0].color;\n"); + } + if (hasNormal) { + // Curved surface is probably always need to compute normal(not sampling from control points) + if (doBezier) { + // Bernstein derivative + WRITE(p, " vec2 bernderiv[4];\n"); + WRITE(p, " bernderiv[0] = -3 * (tess_pos - 1) * (tess_pos - 1); \n"); + WRITE(p, " bernderiv[1] = 9 * tess_pos * tess_pos - 12 * tess_pos + 3; \n"); + WRITE(p, " bernderiv[2] = 3 * (2 - 3 * tess_pos) * tess_pos; \n"); + WRITE(p, " bernderiv[3] = 3 * tess_pos * tess_pos; \n"); - WRITE(p, " vec3 du = pos_r - pos_l;\n"); - WRITE(p, " vec3 dv = pos_d - pos_u;\n"); + WRITE(p, " vec2 bernderiv_u[4];\n"); + WRITE(p, " vec2 bernderiv_v[4];\n"); + WRITE(p, " for (int i = 0; i < 4; i++) {\n"); + WRITE(p, " bernderiv_u[i] = vec2(bernderiv[i].x, weights[i].y);\n"); + WRITE(p, " bernderiv_v[i] = vec2(weights[i].x, bernderiv[i].y);\n"); + WRITE(p, " }\n"); + + WRITE(p, " vec3 du = tess_sample(_pos, bernderiv_u);\n"); + WRITE(p, " vec3 dv = tess_sample(_pos, bernderiv_v);\n"); + } else { // Spline + WRITE(p, " vec2 tess_next_u = vec2(normal.x, 0);\n"); + WRITE(p, " vec2 tess_next_v = vec2(0, normal.y);\n"); + // Right + WRITE(p, " vec2 tess_pos_r = tess_pos + tess_next_u;\n"); + WRITE(p, " spline_weight(tess_pos_r + patch_pos, knots, weights);\n"); + WRITE(p, " vec3 pos_r = tess_sample(_pos, weights);\n"); + // Left + WRITE(p, " vec2 tess_pos_l = tess_pos - tess_next_u;\n"); + WRITE(p, " spline_weight(tess_pos_l + patch_pos, knots, weights);\n"); + WRITE(p, " vec3 pos_l = tess_sample(_pos, weights);\n"); + // Down + WRITE(p, " vec2 tess_pos_d = tess_pos + tess_next_v;\n"); + WRITE(p, " spline_weight(tess_pos_d + patch_pos, knots, weights);\n"); + WRITE(p, " vec3 pos_d = tess_sample(_pos, weights);\n"); + // Up + WRITE(p, " vec2 tess_pos_u = tess_pos - tess_next_v;\n"); + WRITE(p, " spline_weight(tess_pos_u + patch_pos, knots, weights);\n"); + WRITE(p, " vec3 pos_u = tess_sample(_pos, weights);\n"); + + WRITE(p, " vec3 du = pos_r - pos_l;\n"); + WRITE(p, " vec3 dv = pos_d - pos_u;\n"); + } + WRITE(p, " vec3 nrm = cross(du, dv);\n"); + WRITE(p, " nrm = normalize(nrm);\n"); + } + WRITE(p, " vec3 worldpos = vec4(pos.xyz, 1.0) * base.world_mtx;\n"); + if (hasNormal) { + WRITE(p, " mediump vec3 worldnormal = normalize(vec4(%snrm, 0.0) * base.world_mtx);\n", flipNormalTess ? "-" : ""); + } else { + WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n"); } - WRITE(p, " vec3 nrm = cross(du, dv);\n"); - WRITE(p, " nrm = normalize(nrm);\n"); - } - WRITE(p, " vec3 worldpos = vec4(pos.xyz, 1.0) * base.world_mtx;\n"); - if (hasNormal) { - WRITE(p, " mediump vec3 worldnormal = normalize(vec4(%snrm, 0.0) * base.world_mtx);\n", flipNormalTess ? "-" : ""); } else { - WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n"); + // No skinning, just standard T&L. + WRITE(p, " vec3 worldpos = vec4(position.xyz, 1.0) * base.world_mtx;\n"); + if (hasNormal) + WRITE(p, " mediump vec3 worldnormal = normalize(vec4(%snormal, 0.0) * base.world_mtx);\n", flipNormal ? "-" : ""); + else + WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n"); } - } else { - WRITE(p, " vec3 worldpos = vec4(position.xyz, 1.0) * base.world_mtx;\n"); - if (hasNormal) - WRITE(p, " mediump vec3 worldnormal = normalize(vec4(%snormal, 0.0) * base.world_mtx);\n", flipNormal ? "-" : ""); - else - WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n"); } WRITE(p, " vec4 viewPos = vec4(vec4(worldpos, 1.0) * base.view_mtx, 1.0);\n"); From 7de3edb46cdcba7effcfe45c7abc324abd932c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Apr 2018 12:22:26 +0200 Subject: [PATCH 14/40] Revert "Remove gstate_c.deferredVertTypeDirty" This reverts commit fcdb816235d46e582f797e41d7ee344c81194cd4. --- GPU/GPUCommon.cpp | 6 ++++++ GPU/GPUState.h | 1 + 2 files changed, 7 insertions(+) diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 60eace01ac..83afe4b40c 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1428,6 +1428,12 @@ void GPUCommon::Execute_VertexType(u32 op, u32 diff) { gstate.vertType ^= diff; if (diff & (GE_VTYPE_TC_MASK | GE_VTYPE_THROUGH_MASK)) gstate_c.Dirty(DIRTY_UVSCALEOFFSET); + // In this case, we may be doing weights and morphs. + // Update any bone matrix uniforms so it uses them correctly. + if ((op & GE_VTYPE_MORPHCOUNT_MASK) != 0) { + gstate_c.Dirty(gstate_c.deferredVertTypeDirty); + gstate_c.deferredVertTypeDirty = 0; + } gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); } if (diff & GE_VTYPE_THROUGH_MASK) diff --git a/GPU/GPUState.h b/GPU/GPUState.h index 8e6a7c8665..18d8cdd21b 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -553,6 +553,7 @@ struct GPUStateCache { bool allowShaderBlend; float morphWeights[8]; + u32 deferredVertTypeDirty; u32 curTextureWidth; u32 curTextureHeight; From 36fd2711d6846a09b99e3f62dc7f88c9b66de545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Apr 2018 12:22:02 +0200 Subject: [PATCH 15/40] Revert "Remove further remains of hardware skinning." This reverts commit 40db61a6808cb7bab534d6de9010ccce7212c8ee. --- Core/Config.cpp | 1 + Core/Config.h | 1 + GPU/Common/DrawEngineCommon.cpp | 59 +++++++++- GPU/Common/ShaderCommon.h | 12 +- GPU/Common/ShaderId.cpp | 11 ++ GPU/Common/ShaderId.h | 6 +- GPU/Common/ShaderUniforms.cpp | 8 ++ GPU/Common/ShaderUniforms.h | 17 +++ GPU/Common/SoftwareTransformCommon.cpp | 50 +++++++-- GPU/Common/VertexDecoderArm.cpp | 2 +- GPU/Common/VertexDecoderArm64.cpp | 2 +- GPU/Common/VertexDecoderX86.cpp | 2 +- GPU/D3D11/DrawEngineD3D11.cpp | 2 +- GPU/D3D11/GPU_D3D11.cpp | 2 + GPU/D3D11/ShaderManagerD3D11.cpp | 15 ++- GPU/D3D11/ShaderManagerD3D11.h | 2 + GPU/Directx9/DrawEngineDX9.cpp | 2 +- GPU/Directx9/GPU_DX9.cpp | 4 + GPU/Directx9/ShaderManagerDX9.cpp | 34 +++++- GPU/Directx9/VertexShaderGeneratorDX9.cpp | 99 +++++++++++++++- GPU/Directx9/VertexShaderGeneratorDX9.h | 9 ++ GPU/GLES/DrawEngineGLES.cpp | 4 +- GPU/GLES/GPU_GLES.cpp | 4 + GPU/GLES/ShaderManagerGLES.cpp | 22 +++- GPU/GLES/ShaderManagerGLES.h | 7 ++ GPU/GLES/VertexShaderGeneratorGLES.cpp | 125 ++++++++++++++++++++- GPU/GLES/VertexShaderGeneratorGLES.h | 2 + GPU/GPUCommon.cpp | 86 ++++++++++++-- GPU/GPUCommon.h | 3 + GPU/GPUState.cpp | 7 ++ GPU/GPUState.h | 5 + GPU/Software/SoftGpu.cpp | 8 ++ GPU/Software/TransformUnit.cpp | 21 ++++ GPU/Vulkan/DrawEngineVulkan.cpp | 53 ++++++--- GPU/Vulkan/DrawEngineVulkan.h | 9 +- GPU/Vulkan/GPU_Vulkan.cpp | 2 + GPU/Vulkan/ShaderManagerVulkan.cpp | 4 + GPU/Vulkan/ShaderManagerVulkan.h | 6 + GPU/Vulkan/VertexShaderGeneratorVulkan.cpp | 52 ++++++++- UI/GameSettingsScreen.cpp | 7 ++ ext/native/thin3d/VulkanQueueRunner.h | 2 +- headless/Headless.cpp | 1 + unittest/TestVertexJit.cpp | 3 + 43 files changed, 716 insertions(+), 57 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 623637999a..e2bde296d9 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -510,6 +510,7 @@ static ConfigSetting graphicsSettings[] = { ReportedConfigSetting("RenderingMode", &g_Config.iRenderingMode, &DefaultRenderingMode, true, true), ConfigSetting("SoftwareRenderer", &g_Config.bSoftwareRendering, false, true, true), ReportedConfigSetting("HardwareTransform", &g_Config.bHardwareTransform, true, true, true), + ReportedConfigSetting("SoftwareSkinning", &g_Config.bSoftwareSkinning, true, true, true), ReportedConfigSetting("TextureFiltering", &g_Config.iTexFiltering, 1, true, true), ReportedConfigSetting("BufferFiltering", &g_Config.iBufFilter, 1, true, true), ReportedConfigSetting("InternalResolution", &g_Config.iInternalResolution, &DefaultInternalResolution, true, true), diff --git a/Core/Config.h b/Core/Config.h index e22e78f501..466624ce67 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -155,6 +155,7 @@ public: int iGPUBackend; bool bSoftwareRendering; bool bHardwareTransform; // only used in the GLES backend + bool bSoftwareSkinning; // may speed up some games int iRenderingMode; // 0 = non-buffered rendering 1 = buffered rendering int iTexFiltering; // 1 = off , 2 = nearest , 3 = linear , 4 = linear(CG) diff --git a/GPU/Common/DrawEngineCommon.cpp b/GPU/Common/DrawEngineCommon.cpp index 663c782dfd..8903137d0e 100644 --- a/GPU/Common/DrawEngineCommon.cpp +++ b/GPU/Common/DrawEngineCommon.cpp @@ -379,9 +379,13 @@ bool DrawEngineCommon::GetCurrentSimpleVertices(int count, std::vectorDecodeVerts(bufPtr, inPtr, lowerBound, upperBound); + // OK, morphing eliminated but bones still remain to be taken care of. + // Let's do a partial software transform where we only do skinning. + VertexReader reader(bufPtr, dec->GetDecVtxFmt(), vertType); SimpleVertex *sverts = (SimpleVertex *)outPtr; @@ -393,7 +397,56 @@ u32 DrawEngineCommon::NormalizeVertices(u8 *outPtr, u8 *bufPtr, const u8 *inPtr, (u8)gstate.getMaterialAmbientA(), }; - { + // Let's have two separate loops, one for non skinning and one for skinning. + if (!g_Config.bSoftwareSkinning && (vertType & GE_VTYPE_WEIGHT_MASK) != GE_VTYPE_WEIGHT_NONE) { + int numBoneWeights = vertTypeGetNumBoneWeights(vertType); + for (int i = lowerBound; i <= upperBound; i++) { + reader.Goto(i - lowerBound); + SimpleVertex &sv = sverts[i]; + if (vertType & GE_VTYPE_TC_MASK) { + reader.ReadUV(sv.uv); + } + + if (vertType & GE_VTYPE_COL_MASK) { + reader.ReadColor0_8888(sv.color); + } else { + memcpy(sv.color, defaultColor, 4); + } + + float nrm[3], pos[3]; + float bnrm[3], bpos[3]; + + if (vertType & GE_VTYPE_NRM_MASK) { + // Normals are generated during tessellation anyway, not sure if any need to supply + reader.ReadNrm(nrm); + } else { + nrm[0] = 0; + nrm[1] = 0; + nrm[2] = 1.0f; + } + reader.ReadPos(pos); + + // Apply skinning transform directly + float weights[8]; + reader.ReadWeights(weights); + // Skinning + Vec3Packedf psum(0, 0, 0); + Vec3Packedf nsum(0, 0, 0); + for (int w = 0; w < numBoneWeights; w++) { + if (weights[w] != 0.0f) { + Vec3ByMatrix43(bpos, pos, gstate.boneMatrix + w * 12); + Vec3Packedf tpos(bpos); + psum += tpos * weights[w]; + + Norm3ByMatrix43(bnrm, nrm, gstate.boneMatrix + w * 12); + Vec3Packedf tnorm(bnrm); + nsum += tnorm * weights[w]; + } + } + sv.pos = psum; + sv.nrm = nsum; + } + } else { for (int i = lowerBound; i <= upperBound; i++) { reader.Goto(i - lowerBound); SimpleVertex &sv = sverts[i]; @@ -655,7 +708,7 @@ void DrawEngineCommon::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, numDrawCalls++; vertexCountInDrawCalls_ += vertexCount; - if (vertTypeID & GE_VTYPE_WEIGHT_MASK) { + if (g_Config.bSoftwareSkinning && (vertTypeID & GE_VTYPE_WEIGHT_MASK)) { DecodeVertsStep(decoded, decodeCounter_, decodedVerts_); decodeCounter_++; } diff --git a/GPU/Common/ShaderCommon.h b/GPU/Common/ShaderCommon.h index 8de756237c..6e4bfa8c81 100644 --- a/GPU/Common/ShaderCommon.h +++ b/GPU/Common/ShaderCommon.h @@ -76,8 +76,14 @@ enum : uint64_t { DIRTY_WORLDMATRIX = 1ULL << 21, DIRTY_VIEWMATRIX = 1ULL << 22, DIRTY_TEXMATRIX = 1ULL << 23, - - // 8 free bits here where bones used to be! + DIRTY_BONEMATRIX0 = 1ULL << 24, // NOTE: These must be under 32 + DIRTY_BONEMATRIX1 = 1ULL << 25, + DIRTY_BONEMATRIX2 = 1ULL << 26, + DIRTY_BONEMATRIX3 = 1ULL << 27, + DIRTY_BONEMATRIX4 = 1ULL << 28, + DIRTY_BONEMATRIX5 = 1ULL << 29, + DIRTY_BONEMATRIX6 = 1ULL << 30, + DIRTY_BONEMATRIX7 = 1ULL << 31, // These are for hardware tessellation DIRTY_BEZIERSPLINE = 1ULL << 32, @@ -85,6 +91,8 @@ enum : uint64_t { // space for 7 more uniforms. + DIRTY_BONE_UNIFORMS = 0xFF000000ULL, + DIRTY_ALL_UNIFORMS = 0x3FFFFFFFFULL, DIRTY_ALL_LIGHTS = DIRTY_LIGHT0 | DIRTY_LIGHT1 | DIRTY_LIGHT2 | DIRTY_LIGHT3, diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index 8a1667f181..3a8f0de37e 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -33,6 +33,7 @@ std::string VertexShaderDesc(const ShaderID &id) { int ls1 = id.Bits(VS_BIT_LS1, 2); if (uvgMode) desc << uvgModes[uvgMode]; + if (id.Bit(VS_BIT_ENABLE_BONES)) desc << "Bones:" << (id.Bits(VS_BIT_BONES, 3) + 1) << " "; // Lights if (id.Bit(VS_BIT_LIGHTING_ENABLE)) { desc << "Light: "; @@ -102,6 +103,16 @@ void ComputeVertexShaderID(ShaderID *id_out, u32 vertType, bool useHWTransform) id.SetBits(VS_BIT_LS1, 2, gstate.getUVLS1()); } + // Bones. + bool enableBones = vertTypeIsSkinningEnabled(vertType); + id.SetBit(VS_BIT_ENABLE_BONES, enableBones); + if (enableBones) { + id.SetBits(VS_BIT_BONES, 3, TranslateNumBones(vertTypeGetNumBoneWeights(vertType)) - 1); + // 2 bits. We should probably send in the weight scalefactor as a uniform instead, + // or simply preconvert all weights to floats. + id.SetBits(VS_BIT_WEIGHT_FMTSCALE, 2, (vertType & GE_VTYPE_WEIGHT_MASK) >> GE_VTYPE_WEIGHT_SHIFT); + } + // Okay, d[1] coming up. ============== if (gstate.isLightingEnabled() || doShadeMapping) { // doShadeMapping is stored as UVGenMode, so this is enough for isLightingEnabled. diff --git a/GPU/Common/ShaderId.h b/GPU/Common/ShaderId.h index 8ef501a571..afc7c51313 100644 --- a/GPU/Common/ShaderId.h +++ b/GPU/Common/ShaderId.h @@ -7,6 +7,7 @@ // TODO: There will be additional bits, indicating that groups of these will be // sent to the shader and processed there. This will cut down the number of shaders ("ubershader approach") +// This is probably only really worth doing for lighting and bones. enum { VS_BIT_LMODE = 0, VS_BIT_IS_THROUGH = 1, @@ -28,7 +29,10 @@ enum { VS_BIT_UVPROJ_MODE = 18, // 2, can overlap with LS0 VS_BIT_LS0 = 18, // 2 VS_BIT_LS1 = 20, // 2 - // 22 - 31 are free. + VS_BIT_BONES = 22, // 3 should be enough, not 8 + // 25 - 29 are free. + VS_BIT_ENABLE_BONES = 30, + // 31 is free. VS_BIT_LIGHT0_COMP = 32, // 2 bits VS_BIT_LIGHT0_TYPE = 34, // 2 bits VS_BIT_LIGHT1_COMP = 36, // 2 bits diff --git a/GPU/Common/ShaderUniforms.cpp b/GPU/Common/ShaderUniforms.cpp index 5e0927410d..bc02f60912 100644 --- a/GPU/Common/ShaderUniforms.cpp +++ b/GPU/Common/ShaderUniforms.cpp @@ -246,3 +246,11 @@ void LightUpdateUniforms(UB_VS_Lights *ub, uint64_t dirtyUniforms) { } } } + +void BoneUpdateUniforms(UB_VS_Bones *ub, uint64_t dirtyUniforms) { + for (int i = 0; i < 8; i++) { + if (dirtyUniforms & (DIRTY_BONEMATRIX0 << i)) { + ConvertMatrix4x3To3x4Transposed(ub->bones[i], gstate.boneMatrix + 12 * i); + } + } +} diff --git a/GPU/Common/ShaderUniforms.h b/GPU/Common/ShaderUniforms.h index 9e7d028c7a..55cdaf6efd 100644 --- a/GPU/Common/ShaderUniforms.h +++ b/GPU/Common/ShaderUniforms.h @@ -159,5 +159,22 @@ R"( float4 u_ambient; float3 u_lightspecular3; )"; +// With some cleverness, we could get away with uploading just half this when only the four or five first +// bones are being used. This is 512b, 256b would be great. +struct UB_VS_Bones { + float bones[8][12]; +}; + +static const char *ub_vs_bonesStr = +R"( mat3x4 m[8]; +)"; + +// HLSL code is shared so these names are changed to match those in DX9. +static const char *cb_vs_bonesStr = +R"( float4x3 u_bone[8]; +)"; + void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipViewport); void LightUpdateUniforms(UB_VS_Lights *ub, uint64_t dirtyUniforms); +void BoneUpdateUniforms(UB_VS_Bones *ub, uint64_t dirtyUniforms); + diff --git a/GPU/Common/SoftwareTransformCommon.cpp b/GPU/Common/SoftwareTransformCommon.cpp index 5ebf13570c..a29c987c11 100644 --- a/GPU/Common/SoftwareTransformCommon.cpp +++ b/GPU/Common/SoftwareTransformCommon.cpp @@ -144,6 +144,8 @@ void SoftwareTransform( vscale /= gstate_c.curTextureHeight; } + bool skinningEnabled = vertTypeIsSkinningEnabled(vertType); + const int w = gstate.getTextureWidth(0); const int h = gstate.getTextureHeight(0); float widthFactor = (float) w / (float) gstate_c.curTextureWidth; @@ -211,14 +213,48 @@ void SoftwareTransform( Vec3f worldnormal(0, 0, 1); reader.ReadPos(pos); - Vec3ByMatrix43(out, pos, gstate.worldMatrix); - if (reader.hasNormal()) { - reader.ReadNrm(normal.AsArray()); - if (gstate.areNormalsReversed()) { - normal = -normal; + if (!skinningEnabled) { + Vec3ByMatrix43(out, pos, gstate.worldMatrix); + if (reader.hasNormal()) { + reader.ReadNrm(normal.AsArray()); + if (gstate.areNormalsReversed()) { + normal = -normal; + } + Norm3ByMatrix43(worldnormal.AsArray(), normal.AsArray(), gstate.worldMatrix); + worldnormal = worldnormal.Normalized(); + } + } else { + float weights[8]; + reader.ReadWeights(weights); + if (reader.hasNormal()) + reader.ReadNrm(normal.AsArray()); + + // Skinning + Vec3f psum(0, 0, 0); + Vec3f nsum(0, 0, 0); + for (int i = 0; i < vertTypeGetNumBoneWeights(vertType); i++) { + if (weights[i] != 0.0f) { + Vec3ByMatrix43(out, pos, gstate.boneMatrix+i*12); + Vec3f tpos(out); + psum += tpos * weights[i]; + if (reader.hasNormal()) { + Vec3f norm; + Norm3ByMatrix43(norm.AsArray(), normal.AsArray(), gstate.boneMatrix+i*12); + nsum += norm * weights[i]; + } + } + } + + // Yes, we really must multiply by the world matrix too. + Vec3ByMatrix43(out, psum.AsArray(), gstate.worldMatrix); + if (reader.hasNormal()) { + normal = nsum; + if (gstate.areNormalsReversed()) { + normal = -normal; + } + Norm3ByMatrix43(worldnormal.AsArray(), normal.AsArray(), gstate.worldMatrix); + worldnormal = worldnormal.Normalized(); } - Norm3ByMatrix43(worldnormal.AsArray(), normal.AsArray(), gstate.worldMatrix); - worldnormal = worldnormal.Normalized(); } // Perform lighting here if enabled. don't need to check through, it's checked above. diff --git a/GPU/Common/VertexDecoderArm.cpp b/GPU/Common/VertexDecoderArm.cpp index c7cddcb4de..79e6584ae2 100644 --- a/GPU/Common/VertexDecoderArm.cpp +++ b/GPU/Common/VertexDecoderArm.cpp @@ -229,7 +229,7 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int // Add code to convert matrices to 4x4. // Later we might want to do this when the matrices are loaded instead. int boneCount = 0; - if (NEONSkinning && dec.weighttype) { + if (NEONSkinning && dec.weighttype && g_Config.bSoftwareSkinning && dec.morphcount == 1) { // Copying from R3 to R4 MOVP2R(R3, gstate.boneMatrix); MOVP2R(R4, bones); diff --git a/GPU/Common/VertexDecoderArm64.cpp b/GPU/Common/VertexDecoderArm64.cpp index 3c87a6b677..acfa230ea9 100644 --- a/GPU/Common/VertexDecoderArm64.cpp +++ b/GPU/Common/VertexDecoderArm64.cpp @@ -193,7 +193,7 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int // Add code to convert matrices to 4x4. // Later we might want to do this when the matrices are loaded instead. int boneCount = 0; - if (dec.weighttype) { + if (dec.weighttype && g_Config.bSoftwareSkinning && dec.morphcount == 1) { // Copying from R3 to R4 MOVP2R(X3, gstate.boneMatrix); MOVP2R(X4, bones); diff --git a/GPU/Common/VertexDecoderX86.cpp b/GPU/Common/VertexDecoderX86.cpp index f18d24a93d..3b6b22ae16 100644 --- a/GPU/Common/VertexDecoderX86.cpp +++ b/GPU/Common/VertexDecoderX86.cpp @@ -202,7 +202,7 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int // Add code to convert matrices to 4x4. // Later we might want to do this when the matrices are loaded instead. int boneCount = 0; - if (dec.weighttype) { + if (dec.weighttype && g_Config.bSoftwareSkinning && dec.morphcount == 1) { MOV(PTRBITS, R(tempReg1), ImmPtr(&threeMasks)); MOVAPS(XMM4, MatR(tempReg1)); MOV(PTRBITS, R(tempReg1), ImmPtr(&aOne)); diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index 55006baca7..2b49ae2d6e 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -344,7 +344,7 @@ void DrawEngineD3D11::DoFlush() { // Cannot cache vertex data with morph enabled. bool useCache = g_Config.bVertexCache && !(lastVType_ & GE_VTYPE_MORPHCOUNT_MASK); // Also avoid caching when software skinning. - if (lastVType_ & GE_VTYPE_WEIGHT_MASK) + if (g_Config.bSoftwareSkinning && (lastVType_ & GE_VTYPE_WEIGHT_MASK)) useCache = false; if (useCache) { diff --git a/GPU/D3D11/GPU_D3D11.cpp b/GPU/D3D11/GPU_D3D11.cpp index 2b5d760302..8ad9ebd11a 100644 --- a/GPU/D3D11/GPU_D3D11.cpp +++ b/GPU/D3D11/GPU_D3D11.cpp @@ -103,6 +103,7 @@ GPU_D3D11::GPU_D3D11(GraphicsContext *gfxCtx, Draw::DrawContext *draw) // No need to flush before the tex scale/offset commands if we are baking // the tex scale/offset into the vertices anyway. + UpdateCmdInfo(); CheckGPUFeatures(); BuildReportingInfo(); @@ -214,6 +215,7 @@ void GPU_D3D11::InitClear() { void GPU_D3D11::BeginHostFrame() { GPUCommon::BeginHostFrame(); + UpdateCmdInfo(); if (resized_) { CheckGPUFeatures(); framebufferManager_->Resized(); diff --git a/GPU/D3D11/ShaderManagerD3D11.cpp b/GPU/D3D11/ShaderManagerD3D11.cpp index 7e79d57224..c5c498f5f5 100644 --- a/GPU/D3D11/ShaderManagerD3D11.cpp +++ b/GPU/D3D11/ShaderManagerD3D11.cpp @@ -93,19 +93,24 @@ ShaderManagerD3D11::ShaderManagerD3D11(ID3D11Device *device, ID3D11DeviceContext codeBuffer_ = new char[16384]; memset(&ub_base, 0, sizeof(ub_base)); memset(&ub_lights, 0, sizeof(ub_lights)); + memset(&ub_bones, 0, sizeof(ub_bones)); INFO_LOG(G3D, "sizeof(ub_base): %d", (int)sizeof(ub_base)); INFO_LOG(G3D, "sizeof(ub_lights): %d", (int)sizeof(ub_lights)); + INFO_LOG(G3D, "sizeof(ub_bones): %d", (int)sizeof(ub_bones)); D3D11_BUFFER_DESC desc{sizeof(ub_base), D3D11_USAGE_DYNAMIC, D3D11_BIND_CONSTANT_BUFFER, D3D11_CPU_ACCESS_WRITE }; ASSERT_SUCCESS(device_->CreateBuffer(&desc, nullptr, &push_base)); desc.ByteWidth = sizeof(ub_lights); ASSERT_SUCCESS(device_->CreateBuffer(&desc, nullptr, &push_lights)); + desc.ByteWidth = sizeof(ub_bones); + ASSERT_SUCCESS(device_->CreateBuffer(&desc, nullptr, &push_bones)); } ShaderManagerD3D11::~ShaderManagerD3D11() { push_base->Release(); push_lights->Release(); + push_bones->Release(); ClearShaders(); delete[] codeBuffer_; } @@ -154,15 +159,21 @@ uint64_t ShaderManagerD3D11::UpdateUniforms() { memcpy(map.pData, &ub_lights, sizeof(ub_lights)); context_->Unmap(push_lights, 0); } + if (dirty & DIRTY_BONE_UNIFORMS) { + BoneUpdateUniforms(&ub_bones, dirty); + context_->Map(push_bones, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, &ub_bones, sizeof(ub_bones)); + context_->Unmap(push_bones, 0); + } } gstate_c.CleanUniforms(); return dirty; } void ShaderManagerD3D11::BindUniforms() { - ID3D11Buffer *vs_cbs[2] = { push_base, push_lights }; + ID3D11Buffer *vs_cbs[3] = { push_base, push_lights, push_bones }; ID3D11Buffer *ps_cbs[1] = { push_base }; - context_->VSSetConstantBuffers(0, 2, vs_cbs); + context_->VSSetConstantBuffers(0, 3, vs_cbs); context_->PSSetConstantBuffers(0, 1, ps_cbs); } diff --git a/GPU/D3D11/ShaderManagerD3D11.h b/GPU/D3D11/ShaderManagerD3D11.h index 4d08c35dc4..23ff3be8a5 100644 --- a/GPU/D3D11/ShaderManagerD3D11.h +++ b/GPU/D3D11/ShaderManagerD3D11.h @@ -121,10 +121,12 @@ private: // Uniform block scratchpad. These (the relevant ones) are copied to the current pushbuffer at draw time. UB_VS_FS_Base ub_base; UB_VS_Lights ub_lights; + UB_VS_Bones ub_bones; // Not actual pushbuffers, requires D3D11.1, let's try to live without that first. ID3D11Buffer *push_base; ID3D11Buffer *push_lights; + ID3D11Buffer *push_bones; D3D11FragmentShader *lastFShader_; D3D11VertexShader *lastVShader_; diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index 9330893a97..c72d256727 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -325,7 +325,7 @@ void DrawEngineDX9::DoFlush() { // Cannot cache vertex data with morph enabled. bool useCache = g_Config.bVertexCache && !(lastVType_ & GE_VTYPE_MORPHCOUNT_MASK); // Also avoid caching when software skinning. - if (lastVType_ & GE_VTYPE_WEIGHT_MASK) + if (g_Config.bSoftwareSkinning && (lastVType_ & GE_VTYPE_WEIGHT_MASK)) useCache = false; if (useCache) { diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index 52f1a6e2d6..adb50ecbf0 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -83,6 +83,9 @@ GPU_DX9::GPU_DX9(GraphicsContext *gfxCtx, Draw::DrawContext *draw) ERROR_LOG(G3D, "gstate has drifted out of sync!"); } + // No need to flush before the tex scale/offset commands if we are baking + // the tex scale/offset into the vertices anyway. + UpdateCmdInfo(); CheckGPUFeatures(); BuildReportingInfo(); @@ -188,6 +191,7 @@ void GPU_DX9::InitClear() { void GPU_DX9::BeginHostFrame() { GPUCommon::BeginHostFrame(); + UpdateCmdInfo(); if (resized_) { CheckGPUFeatures(); framebufferManager_->Resized(); diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index 10e128d620..70d4cb7693 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -313,7 +313,7 @@ void ShaderManagerDX9::PSUpdateUniforms(u64 dirtyUniforms) { } const uint64_t vsUniforms = DIRTY_PROJMATRIX | DIRTY_PROJTHROUGHMATRIX | DIRTY_WORLDMATRIX | DIRTY_VIEWMATRIX | DIRTY_TEXMATRIX | -DIRTY_FOGCOEF | DIRTY_UVSCALEOFFSET | DIRTY_DEPTHRANGE | +DIRTY_FOGCOEF | DIRTY_BONE_UNIFORMS | DIRTY_UVSCALEOFFSET | DIRTY_DEPTHRANGE | DIRTY_AMBIENT | DIRTY_MATAMBIENTALPHA | DIRTY_MATSPECULAR | DIRTY_MATDIFFUSE | DIRTY_MATEMISSIVE | DIRTY_LIGHT0 | DIRTY_LIGHT1 | DIRTY_LIGHT2 | DIRTY_LIGHT3; void ShaderManagerDX9::VSUpdateUniforms(u64 dirtyUniforms) { @@ -382,6 +382,38 @@ void ShaderManagerDX9::VSUpdateUniforms(u64 dirtyUniforms) { #endif VSSetFloatArray(CONST_VS_FOGCOEF, fogcoef, 2); } + // TODO: Could even set all bones in one go if they're all dirty. +#ifdef USE_BONE_ARRAY + if (u_bone != 0) { + float allBones[8 * 16]; + + bool allDirty = true; + for (int i = 0; i < numBones; i++) { + if (dirtyUniforms & (DIRTY_BONEMATRIX0 << i)) { + ConvertMatrix4x3To4x4(allBones + 16 * i, gstate.boneMatrix + 12 * i); + } else { + allDirty = false; + } + } + if (allDirty) { + // Set them all with one call + //glUniformMatrix4fv(u_bone, numBones, GL_FALSE, allBones); + } else { + // Set them one by one. Could try to coalesce two in a row etc but too lazy. + for (int i = 0; i < numBones; i++) { + if (dirtyUniforms & (DIRTY_BONEMATRIX0 << i)) { + //glUniformMatrix4fv(u_bone + i, 1, GL_FALSE, allBones + 16 * i); + } + } + } + } +#else + for (int i = 0; i < 8; i++) { + if (dirtyUniforms & (DIRTY_BONEMATRIX0 << i)) { + VSSetMatrix4x3_3(CONST_VS_BONE0 + 3 * i, gstate.boneMatrix + 12 * i); + } + } +#endif // Texturing if (dirtyUniforms & DIRTY_UVSCALEOFFSET) { diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.cpp b/GPU/Directx9/VertexShaderGeneratorDX9.cpp index 3a9fe06465..ed397500b4 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.cpp +++ b/GPU/Directx9/VertexShaderGeneratorDX9.cpp @@ -37,6 +37,18 @@ namespace DX9 { +static const char * const boneWeightAttrDecl[9] = { + "#ERROR#", + "float a_w1:TEXCOORD1;\n", + "float2 a_w1:TEXCOORD1;\n", + "float3 a_w1:TEXCOORD1;\n", + "float4 a_w1:TEXCOORD1;\n", + "float4 a_w1:TEXCOORD1;\n float a_w2:TEXCOORD2;\n", + "float4 a_w1:TEXCOORD1;\n float2 a_w2:TEXCOORD2;\n", + "float4 a_w1:TEXCOORD1;\n float3 a_w2:TEXCOORD2;\n", + "float4 a_w1:TEXCOORD1;\n float4 a_w2:TEXCOORD2;\n", +}; + enum DoLightComputation { LIGHT_OFF, LIGHT_SHADE, @@ -68,6 +80,7 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage bool flipNormal = id.Bit(VS_BIT_NORM_REVERSE); int ls0 = id.Bits(VS_BIT_LS0, 2); int ls1 = id.Bits(VS_BIT_LS1, 2); + bool enableBones = id.Bit(VS_BIT_ENABLE_BONES); bool enableLighting = id.Bit(VS_BIT_LIGHTING_ENABLE); int matUpdate = id.Bits(VS_BIT_MATERIAL_UPDATE, 3); @@ -91,6 +104,9 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage int numBoneWeights = 0; int boneWeightScale = id.Bits(VS_BIT_WEIGHT_FMTSCALE, 2); + if (enableBones) { + numBoneWeights = 1 + id.Bits(VS_BIT_BONES, 3); + } if (lang == HLSL_DX9) { WRITE(p, "#pragma warning( disable : 3571 )\n"); @@ -113,6 +129,15 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage WRITE(p, "float4x3 u_view : register(c%i);\n", CONST_VS_VIEW); if (doTextureTransform) WRITE(p, "float4x3 u_tex : register(c%i);\n", CONST_VS_TEXMTX); + if (enableBones) { +#ifdef USE_BONE_ARRAY + WRITE(p, "float4x3 u_bone[%i] : register(c%i);\n", numBones, CONST_VS_BONE0); +#else + for (int i = 0; i < numBoneWeights; i++) { + WRITE(p, "float4x3 u_bone%i : register(c%i);\n", i, CONST_VS_BONE0 + i * 3); + } +#endif + } if (doTexture) { WRITE(p, "float4 u_uvscaleoffset : register(c%i);\n", CONST_VS_UVSCALEOFFSET); } @@ -156,6 +181,7 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage } else { WRITE(p, "cbuffer base : register(b0) {\n%s};\n", cb_baseStr); WRITE(p, "cbuffer lights: register(b1) {\n%s};\n", cb_vs_lightsStr); + WRITE(p, "cbuffer bones : register(b2) {\n%s};\n", cb_vs_bonesStr); } // And the "varyings". @@ -165,6 +191,9 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage if ((doSpline || doBezier) && lang == HLSL_D3D11) { WRITE(p, " uint instanceId : SV_InstanceID;\n"); } + if (enableBones) { + WRITE(p, " %s", boneWeightAttrDecl[numBoneWeights]); + } if (doTexture && hasTexcoord) { WRITE(p, " float2 texcoord : TEXCOORD0;\n"); } @@ -358,7 +387,7 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage } } else { // Step 1: World Transform / Skinning - if (true) { + if (!enableBones) { // Hardware tessellation if (doSpline || doBezier) { WRITE(p, " uint num_patches_u = %s;\n", doBezier ? "(u_spline_count_u - 1) / 3u" : "u_spline_count_u - 3"); @@ -467,6 +496,74 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage else WRITE(p, " float3 worldnormal = float3(0.0, 0.0, 1.0);\n"); } + } else { + static const char * const boneWeightAttr[8] = { + "a_w1.x", "a_w1.y", "a_w1.z", "a_w1.w", + "a_w2.x", "a_w2.y", "a_w2.z", "a_w2.w", + }; + +#if defined(USE_FOR_LOOP) && defined(USE_BONE_ARRAY) + + // To loop through the weights, we unfortunately need to put them in a float array. + // GLSL ES sucks - no way to directly initialize an array! + switch (numBoneWeights) { + case 1: WRITE(p, " float w[1]; w[0] = a_w1;\n"); break; + case 2: WRITE(p, " float w[2]; w[0] = a_w1.x; w[1] = a_w1.y;\n"); break; + case 3: WRITE(p, " float w[3]; w[0] = a_w1.x; w[1] = a_w1.y; w[2] = a_w1.z;\n"); break; + case 4: WRITE(p, " float w[4]; w[0] = a_w1.x; w[1] = a_w1.y; w[2] = a_w1.z; w[3] = a_w1.w;\n"); break; + case 5: WRITE(p, " float w[5]; w[0] = a_w1.x; w[1] = a_w1.y; w[2] = a_w1.z; w[3] = a_w1.w; w[4] = a_w2;\n"); break; + case 6: WRITE(p, " float w[6]; w[0] = a_w1.x; w[1] = a_w1.y; w[2] = a_w1.z; w[3] = a_w1.w; w[4] = a_w2.x; w[5] = a_w2.y;\n"); break; + case 7: WRITE(p, " float w[7]; w[0] = a_w1.x; w[1] = a_w1.y; w[2] = a_w1.z; w[3] = a_w1.w; w[4] = a_w2.x; w[5] = a_w2.y; w[6] = a_w2.z;\n"); break; + case 8: WRITE(p, " float w[8]; w[0] = a_w1.x; w[1] = a_w1.y; w[2] = a_w1.z; w[3] = a_w1.w; w[4] = a_w2.x; w[5] = a_w2.y; w[6] = a_w2.z; w[7] = a_w2.w;\n"); break; + } + + WRITE(p, " mat4 skinMatrix = w[0] * u_bone[0];\n"); + if (numBoneWeights > 1) { + WRITE(p, " for (int i = 1; i < %i; i++) {\n", numBoneWeights); + WRITE(p, " skinMatrix += w[i] * u_bone[i];\n"); + WRITE(p, " }\n"); + } + +#else + if (lang == HLSL_D3D11 || lang == HLSL_D3D11_LEVEL9) { + if (numBoneWeights == 1) + WRITE(p, " float4x3 skinMatrix = mul(In.a_w1, u_bone[0])"); + else + WRITE(p, " float4x3 skinMatrix = mul(In.a_w1.x, u_bone[0])"); + for (int i = 1; i < numBoneWeights; i++) { + const char *weightAttr = boneWeightAttr[i]; + // workaround for "cant do .x of scalar" issue + if (numBoneWeights == 1 && i == 0) weightAttr = "a_w1"; + if (numBoneWeights == 5 && i == 4) weightAttr = "a_w2"; + WRITE(p, " + mul(In.%s, u_bone[%i])", weightAttr, i); + } + } else { + if (numBoneWeights == 1) + WRITE(p, " float4x3 skinMatrix = mul(In.a_w1, u_bone0)"); + else + WRITE(p, " float4x3 skinMatrix = mul(In.a_w1.x, u_bone0)"); + for (int i = 1; i < numBoneWeights; i++) { + const char *weightAttr = boneWeightAttr[i]; + // workaround for "cant do .x of scalar" issue + if (numBoneWeights == 1 && i == 0) weightAttr = "a_w1"; + if (numBoneWeights == 5 && i == 4) weightAttr = "a_w2"; + WRITE(p, " + mul(In.%s, u_bone%i)", weightAttr, i); + } + } +#endif + + WRITE(p, ";\n"); + + // Trying to simplify this results in bugs in LBP... + WRITE(p, " float3 skinnedpos = mul(float4(In.position.xyz, 1.0), skinMatrix);\n"); + WRITE(p, " float3 worldpos = mul(float4(skinnedpos, 1.0), u_world);\n"); + + if (hasNormal) { + WRITE(p, " float3 skinnednormal = mul(float4(%sIn.normal, 0.0), skinMatrix);\n", flipNormal ? "-" : ""); + } else { + WRITE(p, " float3 skinnednormal = mul(float4(0.0, 0.0, %s1.0, 0.0), skinMatrix);\n", flipNormal ? "-" : ""); + } + WRITE(p, " float3 worldnormal = normalize(mul(float4(skinnednormal, 0.0), u_world));\n"); } WRITE(p, " float4 viewPos = float4(mul(float4(worldpos, 1.0), u_view), 1.0);\n"); diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.h b/GPU/Directx9/VertexShaderGeneratorDX9.h index a76a5ec5ed..e33567992c 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.h +++ b/GPU/Directx9/VertexShaderGeneratorDX9.h @@ -44,6 +44,15 @@ namespace DX9 { CONST_VS_LIGHTSPECULAR = 44, CONST_VS_LIGHTAMBIENT = 48, CONST_VS_DEPTHRANGE = 52, + CONST_VS_BONE0 = 53, + CONST_VS_BONE1 = 56, + CONST_VS_BONE2 = 59, + CONST_VS_BONE3 = 62, + CONST_VS_BONE4 = 65, + CONST_VS_BONE5 = 68, + CONST_VS_BONE6 = 71, + CONST_VS_BONE7 = 74, + CONST_VS_BONE8 = 77, }; }; diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index c304d5d236..25822b470d 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -325,7 +325,7 @@ void DrawEngineGLES::DoFlush() { // Cannot cache vertex data with morph enabled. bool useCache = g_Config.bVertexCache && !(lastVType_ & GE_VTYPE_MORPHCOUNT_MASK); // Also avoid caching when software skinning. - if (lastVType_ & GE_VTYPE_WEIGHT_MASK) + if (g_Config.bSoftwareSkinning && (lastVType_ & GE_VTYPE_WEIGHT_MASK)) useCache = false; // TEMPORARY @@ -469,7 +469,7 @@ void DrawEngineGLES::DoFlush() { vai->lastFrame = gpuStats.numFlips; } else { - if (lastVType_ & GE_VTYPE_WEIGHT_MASK) { + if (g_Config.bSoftwareSkinning && (lastVType_ & GE_VTYPE_WEIGHT_MASK)) { // If software skinning, we've already predecoded into "decoded". So push that content. size_t size = decodedVerts_ * dec_->GetDecVtxFmt().stride; u8 *dest = (u8 *)frameData.pushVertex->Push(size, &vertexBufferOffset, &vertexBuffer); diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 7b64f05d32..b1505cfacd 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -89,6 +89,8 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw) // No need to flush before the tex scale/offset commands if we are baking // the tex scale/offset into the vertices anyway. + UpdateCmdInfo(); + BuildReportingInfo(); // Update again after init to be sure of any silly driver problems. UpdateVsyncInterval(true); @@ -344,6 +346,7 @@ void GPU_GLES::DeviceRestore() { draw_ = (Draw::DrawContext *)PSP_CoreParameter().graphicsContext->GetDrawContext(); ILOG("GPU_GLES: DeviceRestore"); + UpdateCmdInfo(); UpdateVsyncInterval(true); textureCacheGL_->DeviceRestore(draw_); @@ -363,6 +366,7 @@ void GPU_GLES::InitClear() { void GPU_GLES::BeginHostFrame() { GPUCommon::BeginHostFrame(); + UpdateCmdInfo(); if (resized_) { CheckGPUFeatures(); framebufferManager_->Resized(); diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 9425589665..0fee3e6500 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -109,8 +109,21 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, queries.push_back({ &u_world, "u_world" }); queries.push_back({ &u_texmtx, "u_texmtx" }); + if (VSID.Bit(VS_BIT_ENABLE_BONES)) + numBones = TranslateNumBones(VSID.Bits(VS_BIT_BONES, 3) + 1); + else + numBones = 0; queries.push_back({ &u_depthRange, "u_depthRange" }); +#ifdef USE_BONE_ARRAY + queries.push_back({ &u_bone, "u_bone" }); +#else + static const char * const boneNames[8] = { "u_bone0", "u_bone1", "u_bone2", "u_bone3", "u_bone4", "u_bone5", "u_bone6", "u_bone7", }; + for (int i = 0; i < 8; i++) { + queries.push_back({ &u_bone[i], boneNames[i] }); + } +#endif + // Lighting, texturing queries.push_back({ &u_ambient, "u_ambient" }); queries.push_back({ &u_matambientalpha, "u_matambientalpha" }); @@ -465,6 +478,13 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid) { float f = (float)gstate.getStencilTestRef() * (1.0f / 255.0f); render_->SetUniformF(&u_stencilReplaceValue, 1, &f); } + float bonetemp[16]; + for (int i = 0; i < numBones; i++) { + if (dirty & (DIRTY_BONEMATRIX0 << i)) { + ConvertMatrix4x3To4x4(bonetemp, gstate.boneMatrix + 12 * i); + render_->SetUniformM4x4(&u_bone[i], bonetemp); + } + } if (dirty & DIRTY_SHADERBLEND) { if (u_blendFixA != -1) { @@ -790,7 +810,7 @@ std::string ShaderManagerGLES::DebugGetShaderString(std::string id, DebugShaderT // as sometimes these features might have an effect on the ID bits. #define CACHE_HEADER_MAGIC 0x83277592 -#define CACHE_VERSION 10 +#define CACHE_VERSION 11 struct CacheHeader { uint32_t magic; uint32_t version; diff --git a/GPU/GLES/ShaderManagerGLES.h b/GPU/GLES/ShaderManagerGLES.h index 7f19a77e28..e04a9a8c8b 100644 --- a/GPU/GLES/ShaderManagerGLES.h +++ b/GPU/GLES/ShaderManagerGLES.h @@ -72,6 +72,13 @@ public: int u_world; int u_depthRange; // x,y = viewport xscale/xcenter. z,w=clipping minz/maxz (?) +#ifdef USE_BONE_ARRAY + int u_bone; // array, size is numBones +#else + int u_bone[8]; +#endif + int numBones; + // Shader blending. int u_fbotex; int u_blendFixA; diff --git a/GPU/GLES/VertexShaderGeneratorGLES.cpp b/GPU/GLES/VertexShaderGeneratorGLES.cpp index 97e7ab3ef1..edb1828429 100644 --- a/GPU/GLES/VertexShaderGeneratorGLES.cpp +++ b/GPU/GLES/VertexShaderGeneratorGLES.cpp @@ -38,6 +38,30 @@ #define WRITE p+=sprintf +static const char * const boneWeightAttrDecl[9] = { + "#ERROR#", + "attribute mediump float w1;\n", + "attribute mediump vec2 w1;\n", + "attribute mediump vec3 w1;\n", + "attribute mediump vec4 w1;\n", + "attribute mediump vec4 w1;\nattribute mediump float w2;\n", + "attribute mediump vec4 w1;\nattribute mediump vec2 w2;\n", + "attribute mediump vec4 w1;\nattribute mediump vec3 w2;\n", + "attribute mediump vec4 w1, w2;\n", +}; + +static const char * const boneWeightInDecl[9] = { + "#ERROR#", + "in mediump float w1;\n", + "in mediump vec2 w1;\n", + "in mediump vec3 w1;\n", + "in mediump vec4 w1;\n", + "in mediump vec4 w1;\nin mediump float w2;\n", + "in mediump vec4 w1;\nin mediump vec2 w2;\n", + "in mediump vec4 w1;\nin mediump vec3 w2;\n", + "in mediump vec4 w1, w2;\n", +}; + enum DoLightComputation { LIGHT_OFF, LIGHT_SHADE, @@ -81,6 +105,7 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask, bool glslES30 = false; const char *varying = "varying"; const char *attribute = "attribute"; + const char * const * boneWeightDecl = boneWeightAttrDecl; const char *texelFetch = NULL; bool highpFog = false; bool highpTexcoord = false; @@ -133,6 +158,7 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask, if (glslES30 || gl_extensions.IsCoreContext) { attribute = "in"; varying = "out"; + boneWeightDecl = boneWeightInDecl; } bool isModeThrough = id.Bit(VS_BIT_IS_THROUGH); @@ -156,6 +182,7 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask, bool flipNormal = id.Bit(VS_BIT_NORM_REVERSE); int ls0 = id.Bits(VS_BIT_LS0, 2); int ls1 = id.Bits(VS_BIT_LS1, 2); + bool enableBones = id.Bit(VS_BIT_ENABLE_BONES); bool enableLighting = id.Bit(VS_BIT_LIGHTING_ENABLE); int matUpdate = id.Bits(VS_BIT_MATERIAL_UPDATE, 3); @@ -181,6 +208,16 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask, } } + int numBoneWeights = 0; + int boneWeightScale = id.Bits(VS_BIT_WEIGHT_FMTSCALE, 2); + if (enableBones) { + numBoneWeights = 1 + id.Bits(VS_BIT_BONES, 3); + WRITE(p, "%s", boneWeightDecl[numBoneWeights]); + *attrMask |= 1 << ATTR_W1; + if (numBoneWeights >= 5) + *attrMask |= 1 << ATTR_W2; + } + if (useHWTransform) WRITE(p, "%s vec3 position;\n", attribute); else @@ -231,6 +268,17 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask, WRITE(p, "uniform mediump mat4 u_texmtx;\n"); *uniformMask |= DIRTY_TEXMATRIX; } + if (enableBones) { +#ifdef USE_BONE_ARRAY + WRITE(p, "uniform mediump mat4 u_bone[%i];\n", numBoneWeights); + *uniformMask |= DIRTY_BONE_UNIFORMS; +#else + for (int i = 0; i < numBoneWeights; i++) { + WRITE(p, "uniform mat4 u_bone%i;\n", i); + *uniformMask |= DIRTY_BONEMATRIX0 << i; + } +#endif + } if (doTexture) { WRITE(p, "uniform vec4 u_uvscaleoffset;\n"); *uniformMask |= DIRTY_UVSCALEOFFSET; @@ -436,7 +484,7 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask, } } else { // Step 1: World Transform / Skinning - if (true) { + if (!enableBones) { // Hardware tessellation if (doBezier || doSpline) { WRITE(p, " vec3 _pos[16];\n"); @@ -543,6 +591,81 @@ void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask, else WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n"); } + } else { + static const char *rescale[4] = {"", " * 1.9921875", " * 1.999969482421875", ""}; // 2*127.5f/128.f, 2*32767.5f/32768.f, 1.0f}; + const char *factor = rescale[boneWeightScale]; + + static const char * const boneWeightAttr[8] = { + "w1.x", "w1.y", "w1.z", "w1.w", + "w2.x", "w2.y", "w2.z", "w2.w", + }; + +#if defined(USE_FOR_LOOP) && defined(USE_BONE_ARRAY) + + // To loop through the weights, we unfortunately need to put them in a float array. + // GLSL ES sucks - no way to directly initialize an array! + switch (numBoneWeights) { + case 1: WRITE(p, " float w[1]; w[0] = w1;\n"); break; + case 2: WRITE(p, " float w[2]; w[0] = w1.x; w[1] = w1.y;\n"); break; + case 3: WRITE(p, " float w[3]; w[0] = w1.x; w[1] = w1.y; w[2] = w1.z;\n"); break; + case 4: WRITE(p, " float w[4]; w[0] = w1.x; w[1] = w1.y; w[2] = w1.z; w[3] = w1.w;\n"); break; + case 5: WRITE(p, " float w[5]; w[0] = w1.x; w[1] = w1.y; w[2] = w1.z; w[3] = w1.w; w[4] = w2;\n"); break; + case 6: WRITE(p, " float w[6]; w[0] = w1.x; w[1] = w1.y; w[2] = w1.z; w[3] = w1.w; w[4] = w2.x; w[5] = w2.y;\n"); break; + case 7: WRITE(p, " float w[7]; w[0] = w1.x; w[1] = w1.y; w[2] = w1.z; w[3] = w1.w; w[4] = w2.x; w[5] = w2.y; w[6] = w2.z;\n"); break; + case 8: WRITE(p, " float w[8]; w[0] = w1.x; w[1] = w1.y; w[2] = w1.z; w[3] = w1.w; w[4] = w2.x; w[5] = w2.y; w[6] = w2.z; w[7] = w2.w;\n"); break; + } + + WRITE(p, " mat4 skinMatrix = w[0] * u_bone[0];\n"); + if (numBoneWeights > 1) { + WRITE(p, " for (int i = 1; i < %i; i++) {\n", numBoneWeights); + WRITE(p, " skinMatrix += w[i] * u_bone[i];\n"); + WRITE(p, " }\n"); + } + +#else + +#ifdef USE_BONE_ARRAY + if (numBoneWeights == 1) + WRITE(p, " mat4 skinMatrix = w1 * u_bone[0]"); + else + WRITE(p, " mat4 skinMatrix = w1.x * u_bone[0]"); + for (int i = 1; i < numBoneWeights; i++) { + const char *weightAttr = boneWeightAttr[i]; + // workaround for "cant do .x of scalar" issue + if (numBoneWeights == 1 && i == 0) weightAttr = "w1"; + if (numBoneWeights == 5 && i == 4) weightAttr = "w2"; + WRITE(p, " + %s * u_bone[%i]", weightAttr, i); + } +#else + // Uncomment this to screw up bone shaders to check the vertex shader software fallback + // WRITE(p, "THIS SHOULD ERROR! #error"); + if (numBoneWeights == 1) + WRITE(p, " mat4 skinMatrix = w1 * u_bone0"); + else + WRITE(p, " mat4 skinMatrix = w1.x * u_bone0"); + for (int i = 1; i < numBoneWeights; i++) { + const char *weightAttr = boneWeightAttr[i]; + // workaround for "cant do .x of scalar" issue + if (numBoneWeights == 1 && i == 0) weightAttr = "w1"; + if (numBoneWeights == 5 && i == 4) weightAttr = "w2"; + WRITE(p, " + %s * u_bone%i", weightAttr, i); + } +#endif + +#endif + + WRITE(p, ";\n"); + + // Trying to simplify this results in bugs in LBP... + WRITE(p, " vec3 skinnedpos = (skinMatrix * vec4(position, 1.0)).xyz %s;\n", factor); + WRITE(p, " vec3 worldpos = (u_world * vec4(skinnedpos, 1.0)).xyz;\n"); + + if (hasNormal) { + WRITE(p, " mediump vec3 skinnednormal = (skinMatrix * vec4(%snormal, 0.0)).xyz %s;\n", flipNormal ? "-" : "", factor); + } else { + WRITE(p, " mediump vec3 skinnednormal = (skinMatrix * vec4(0.0, 0.0, %s1.0, 0.0)).xyz %s;\n", flipNormal ? "-" : "", factor); + } + WRITE(p, " mediump vec3 worldnormal = normalize((u_world * vec4(skinnednormal, 0.0)).xyz);\n"); } WRITE(p, " vec4 viewPos = u_view * vec4(worldpos, 1.0);\n"); diff --git a/GPU/GLES/VertexShaderGeneratorGLES.h b/GPU/GLES/VertexShaderGeneratorGLES.h index ae78cad767..7801b2cd6f 100644 --- a/GPU/GLES/VertexShaderGeneratorGLES.h +++ b/GPU/GLES/VertexShaderGeneratorGLES.h @@ -19,6 +19,8 @@ #include "Common/CommonTypes.h" +// #define USE_BONE_ARRAY + struct VShaderID; void GenerateVertexShader(const VShaderID &id, char *buffer, uint32_t *attrMask, uint64_t *uniformMask); diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 83afe4b40c..0fb4318eb0 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -46,8 +46,8 @@ const CommonCommandTableEntry commonCommandTable[] = { { GE_CMD_BEZIER, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPUCommon::Execute_Bezier }, { GE_CMD_SPLINE, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPUCommon::Execute_Spline }, - // Changing the vertex type does not always require us to flush so handle that in Execute_VertexType. - { GE_CMD_VERTEXTYPE, FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_VertexType }, + // Changing the vertex type requires us to flush. + { GE_CMD_VERTEXTYPE, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_VertexType }, { GE_CMD_LOADCLUT, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPUCommon::Execute_LoadClut }, @@ -403,11 +403,23 @@ GPUCommon::GPUCommon(GraphicsContext *gfxCtx, Draw::DrawContext *draw) : ERROR_LOG(G3D, "Command missing from table: %02x (%i)", i, i); } } + + UpdateCmdInfo(); } GPUCommon::~GPUCommon() { } +void GPUCommon::UpdateCmdInfo() { + if (g_Config.bSoftwareSkinning) { + cmdInfo_[GE_CMD_VERTEXTYPE].flags &= ~FLAG_FLUSHBEFOREONCHANGE; + cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPUCommon::Execute_VertexTypeSkinning; + } else { + cmdInfo_[GE_CMD_VERTEXTYPE].flags |= FLAG_FLUSHBEFOREONCHANGE; + cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPUCommon::Execute_VertexType; + } +} + void GPUCommon::BeginHostFrame() { ReapplyGfxState(); @@ -1414,12 +1426,22 @@ void GPUCommon::Execute_TexSize0(u32 op, u32 diff) { } } +void GPUCommon::Execute_VertexType(u32 op, u32 diff) { + if (diff) + gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); + if (diff & (GE_VTYPE_TC_MASK | GE_VTYPE_THROUGH_MASK)) { + gstate_c.Dirty(DIRTY_UVSCALEOFFSET); + if (diff & GE_VTYPE_THROUGH_MASK) + gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE); + } +} + void GPUCommon::Execute_LoadClut(u32 op, u32 diff) { gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); textureCache_->LoadClut(gstate.getClutAddress(), gstate.getClutLoadBytes()); } -void GPUCommon::Execute_VertexType(u32 op, u32 diff) { +void GPUCommon::Execute_VertexTypeSkinning(u32 op, u32 diff) { // Don't flush when weight count changes. if (diff & ~GE_VTYPE_WEIGHTCOUNT_MASK) { // Restore and flush @@ -1661,6 +1683,10 @@ void GPUCommon::Execute_Bezier(u32 op, u32 diff) { indices = Memory::GetPointerUnchecked(gstate_c.indexAddr); } + if (vertTypeIsSkinningEnabled(gstate.vertType)) { + DEBUG_LOG_REPORT(G3D, "Unusual bezier/spline vtype: %08x, morph: %d, bones: %d", gstate.vertType, (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT, vertTypeGetNumBoneWeights(gstate.vertType)); + } + GEPatchPrimType patchPrim = gstate.getPatchPrimitiveType(); SetDrawType(DRAW_BEZIER, PatchPrimToPrim(patchPrim)); @@ -1719,6 +1745,10 @@ void GPUCommon::Execute_Spline(u32 op, u32 diff) { indices = Memory::GetPointerUnchecked(gstate_c.indexAddr); } + if (vertTypeIsSkinningEnabled(gstate.vertType)) { + DEBUG_LOG_REPORT(G3D, "Unusual bezier/spline vtype: %08x, morph: %d, bones: %d", gstate.vertType, (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT, vertTypeGetNumBoneWeights(gstate.vertType)); + } + int sp_ucount = op & 0xFF; int sp_vcount = (op >> 8) & 0xFF; int sp_utype = (op >> 16) & 0x3; @@ -1999,10 +2029,34 @@ void GPUCommon::Execute_BoneMtxNum(u32 op, u32 diff) { } if (fastLoad) { - while ((src[i] >> 24) == GE_CMD_BONEMATRIXDATA) { - dst[i] = src[i] << 8; - if (++i >= end) { - break; + // If we can't use software skinning, we have to flush and dirty. + if (!g_Config.bSoftwareSkinning) { + while ((src[i] >> 24) == GE_CMD_BONEMATRIXDATA) { + const u32 newVal = src[i] << 8; + if (dst[i] != newVal) { + Flush(); + dst[i] = newVal; + } + if (++i >= end) { + break; + } + } + + const unsigned int numPlusCount = (op & 0x7F) + i; + for (unsigned int num = op & 0x7F; num < numPlusCount; num += 12) { + gstate_c.Dirty(DIRTY_BONEMATRIX0 << (num / 12)); + } + } else { + while ((src[i] >> 24) == GE_CMD_BONEMATRIXDATA) { + dst[i] = src[i] << 8; + if (++i >= end) { + break; + } + } + + const unsigned int numPlusCount = (op & 0x7F) + i; + for (unsigned int num = op & 0x7F; num < numPlusCount; num += 12) { + gstate_c.deferredVertTypeDirty |= DIRTY_BONEMATRIX0 << (num / 12); } } } @@ -2020,6 +2074,13 @@ void GPUCommon::Execute_BoneMtxData(u32 op, u32 diff) { int num = gstate.boneMatrixNumber & 0x7F; u32 newVal = op << 8; if (num < 96 && newVal != ((const u32 *)gstate.boneMatrix)[num]) { + // Bone matrices should NOT flush when software skinning is enabled! + if (!g_Config.bSoftwareSkinning) { + Flush(); + gstate_c.Dirty(DIRTY_BONEMATRIX0 << (num / 12)); + } else { + gstate_c.deferredVertTypeDirty |= DIRTY_BONEMATRIX0 << (num / 12); + } ((u32 *)gstate.boneMatrix)[num] = newVal; } num++; @@ -2160,6 +2221,17 @@ void GPUCommon::Execute_Unknown(u32 op, u32 diff) { void GPUCommon::FastLoadBoneMatrix(u32 target) { const int num = gstate.boneMatrixNumber & 0x7F; const int mtxNum = num / 12; + uint32_t uniformsToDirty = DIRTY_BONEMATRIX0 << mtxNum; + if ((num - 12 * mtxNum) != 0) { + uniformsToDirty |= DIRTY_BONEMATRIX0 << ((mtxNum + 1) & 7); + } + + if (!g_Config.bSoftwareSkinning) { + Flush(); + gstate_c.Dirty(uniformsToDirty); + } else { + gstate_c.deferredVertTypeDirty |= uniformsToDirty; + } gstate.FastLoadBoneMatrix(target); } diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index 44bb9ab6d2..9d379000e1 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -72,6 +72,8 @@ public: } virtual void CheckGPUFeatures() = 0; + void UpdateCmdInfo(); + bool IsReady() override { return true; } @@ -129,6 +131,7 @@ public: void Execute_End(u32 op, u32 diff); void Execute_VertexType(u32 op, u32 diff); + void Execute_VertexTypeSkinning(u32 op, u32 diff); void Execute_Prim(u32 op, u32 diff); void Execute_Bezier(u32 op, u32 diff); diff --git a/GPU/GPUState.cpp b/GPU/GPUState.cpp index 19ae21706d..7e3f3812ef 100644 --- a/GPU/GPUState.cpp +++ b/GPU/GPUState.cpp @@ -240,6 +240,13 @@ void GPUgstate::Restore(u32_le *ptr) { } } +bool vertTypeIsSkinningEnabled(u32 vertType) { + if (g_Config.bSoftwareSkinning) + return false; + else + return ((vertType & GE_VTYPE_WEIGHT_MASK) != GE_VTYPE_WEIGHT_NONE); +} + struct GPUStateCache_v0 { u32 vertexAddr; u32 indexAddr; diff --git a/GPU/GPUState.h b/GPU/GPUState.h index 18d8cdd21b..58bc5e3859 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -441,6 +441,11 @@ struct GPUgstate { void Restore(u32_le *ptr); }; +bool vertTypeIsSkinningEnabled(u32 vertType); + +inline int vertTypeGetNumBoneWeights(u32 vertType) { return 1 + ((vertType & GE_VTYPE_WEIGHTCOUNT_MASK) >> GE_VTYPE_WEIGHTCOUNT_SHIFT); } +inline int vertTypeGetWeightMask(u32 vertType) { return vertType & GE_VTYPE_WEIGHT_MASK; } + // The rest is cached simplified/converted data for fast access. // Does not need to be saved when saving/restoring context. // diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index da74874c7c..b24d286940 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -396,6 +396,10 @@ void SoftGPU::ExecuteOp(u32 op, u32 diff) { indices = Memory::GetPointerUnchecked(gstate_c.indexAddr); } + if ((gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) || vertTypeIsSkinningEnabled(gstate.vertType)) { + DEBUG_LOG_REPORT(G3D, "Unusual bezier/spline vtype: %08x, morph: %d, bones: %d", gstate.vertType, (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT, vertTypeGetNumBoneWeights(gstate.vertType)); + } + GEPatchPrimType patchPrim = gstate.getPatchPrimitiveType(); SetDrawType(DRAW_BEZIER, PatchPrimToPrim(patchPrim)); @@ -440,6 +444,10 @@ void SoftGPU::ExecuteOp(u32 op, u32 diff) { indices = Memory::GetPointerUnchecked(gstate_c.indexAddr); } + if ((gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) || vertTypeIsSkinningEnabled(gstate.vertType)) { + DEBUG_LOG_REPORT(G3D, "Unusual bezier/spline vtype: %08x, morph: %d, bones: %d", gstate.vertType, (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT, vertTypeGetNumBoneWeights(gstate.vertType)); + } + int sp_ucount = op & 0xFF; int sp_vcount = (op >> 8) & 0xFF; int sp_utype = (op >> 16) & 0x3; diff --git a/GPU/Software/TransformUnit.cpp b/GPU/Software/TransformUnit.cpp index e6bc3fbba0..f1e7e9581b 100644 --- a/GPU/Software/TransformUnit.cpp +++ b/GPU/Software/TransformUnit.cpp @@ -167,6 +167,27 @@ VertexData TransformUnit::ReadVertex(VertexReader& vreader) vertex.normal = -vertex.normal; } + if (vertTypeIsSkinningEnabled(gstate.vertType) && !gstate.isModeThrough()) { + float W[8] = { 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f }; + vreader.ReadWeights(W); + + Vec3 tmppos(0.f, 0.f, 0.f); + Vec3 tmpnrm(0.f, 0.f, 0.f); + + for (int i = 0; i < vertTypeGetNumBoneWeights(gstate.vertType); ++i) { + Mat3x3 bone(&gstate.boneMatrix[12*i]); + tmppos += (bone * ModelCoords(pos[0], pos[1], pos[2]) + Vec3(gstate.boneMatrix[12*i+9], gstate.boneMatrix[12*i+10], gstate.boneMatrix[12*i+11])) * W[i]; + if (vreader.hasNormal()) + tmpnrm += (bone * vertex.normal) * W[i]; + } + + pos[0] = tmppos.x; + pos[1] = tmppos.y; + pos[2] = tmppos.z; + if (vreader.hasNormal()) + vertex.normal = tmpnrm; + } + if (vreader.hasColor0()) { float col[4]; vreader.ReadColor0(col); diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index b261c1cc4c..5b5275d77e 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -66,7 +66,8 @@ enum { DRAW_BINDING_2ND_TEXTURE = 1, DRAW_BINDING_DYNUBO_BASE = 2, DRAW_BINDING_DYNUBO_LIGHT = 3, - DRAW_BINDING_TESS_STORAGE_BUF = 4, + DRAW_BINDING_DYNUBO_BONE = 4, + DRAW_BINDING_TESS_STORAGE_BUF = 5, }; enum { @@ -94,7 +95,7 @@ DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan, Draw::DrawContext *dra void DrawEngineVulkan::InitDeviceObjects() { // All resources we need for PSP drawing. Usually only bindings 0 and 2-4 are populated. - VkDescriptorSetLayoutBinding bindings[5]{}; + VkDescriptorSetLayoutBinding bindings[6]{}; bindings[0].descriptorCount = 1; bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; bindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; @@ -111,11 +112,15 @@ void DrawEngineVulkan::InitDeviceObjects() { bindings[3].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; bindings[3].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; bindings[3].binding = DRAW_BINDING_DYNUBO_LIGHT; - // Used only for hardware tessellation. bindings[4].descriptorCount = 1; - bindings[4].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + bindings[4].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; bindings[4].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - bindings[4].binding = DRAW_BINDING_TESS_STORAGE_BUF; + bindings[4].binding = DRAW_BINDING_DYNUBO_BONE; + // Used only for hardware tessellation. + bindings[5].descriptorCount = 1; + bindings[5].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + bindings[5].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + bindings[5].binding = DRAW_BINDING_TESS_STORAGE_BUF; VkDevice device = vulkan_->GetDevice(); @@ -129,7 +134,7 @@ void DrawEngineVulkan::InitDeviceObjects() { // if creating and updating them turns out to be expensive. for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { // We now create descriptor pools on demand, so removed from here. - frame_[i].pushUBO = new VulkanPushBuffer(vulkan_, 4 * 1024 * 1024); + frame_[i].pushUBO = new VulkanPushBuffer(vulkan_, 8 * 1024 * 1024); frame_[i].pushVertex = new VulkanPushBuffer(vulkan_, 2 * 1024 * 1024); frame_[i].pushIndex = new VulkanPushBuffer(vulkan_, 1 * 1024 * 1024); } @@ -360,7 +365,7 @@ VkResult DrawEngineVulkan::RecreateDescriptorPool(FrameData &frame, int newSize) frame.descPoolSize = newSize; VkDescriptorPoolSize dpTypes[3]; - dpTypes[0].descriptorCount = frame.descPoolSize * 2; + dpTypes[0].descriptorCount = frame.descPoolSize * 3; dpTypes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; dpTypes[1].descriptorCount = frame.descPoolSize * 2; // Don't use these for tess anymore, need max two per set. dpTypes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; @@ -378,15 +383,17 @@ VkResult DrawEngineVulkan::RecreateDescriptorPool(FrameData &frame, int newSize) return res; } -VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView, VkSampler sampler, VkBuffer base, VkBuffer light, bool tess) { +VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView, VkSampler sampler, VkBuffer base, VkBuffer light, VkBuffer bone, bool tess) { DescriptorSetKey key; key.imageView_ = imageView; key.sampler_ = sampler; key.secondaryImageView_ = boundSecondary_; key.base_ = base; key.light_ = light; + key.bone_ = bone; _dbg_assert_(G3D, base != VK_NULL_HANDLE); _dbg_assert_(G3D, light != VK_NULL_HANDLE); + _dbg_assert_(G3D, bone != VK_NULL_HANDLE); FrameData &frame = frame_[vulkan_->GetCurFrame()]; // See if we already have this descriptor set cached. @@ -494,7 +501,7 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView } // Uniform buffer objects - VkDescriptorBufferInfo buf[2]{}; + VkDescriptorBufferInfo buf[3]{}; int count = 0; buf[count].buffer = base; buf[count].offset = 0; @@ -504,6 +511,10 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView buf[count].offset = 0; buf[count].range = sizeof(UB_VS_Lights); count++; + buf[count].buffer = bone; + buf[count].offset = 0; + buf[count].range = sizeof(UB_VS_Bones); + count++; for (int i = 0; i < count; i++) { writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[n].pNext = nullptr; @@ -527,9 +538,11 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView void DrawEngineVulkan::DirtyAllUBOs() { baseUBOOffset = 0; lightUBOOffset = 0; + boneUBOOffset = 0; baseBuf = VK_NULL_HANDLE; lightBuf = VK_NULL_HANDLE; - dirtyUniforms_ = DIRTY_BASE_UNIFORMS | DIRTY_LIGHT_UNIFORMS; + boneBuf = VK_NULL_HANDLE; + dirtyUniforms_ = DIRTY_BASE_UNIFORMS | DIRTY_LIGHT_UNIFORMS | DIRTY_BONE_UNIFORMS; imageView = VK_NULL_HANDLE; sampler = VK_NULL_HANDLE; gstate_c.Dirty(DIRTY_TEXTURE_IMAGE); @@ -588,7 +601,7 @@ void DrawEngineVulkan::DoFlush() { // Also avoid caching when software skinning. VkBuffer vbuf = VK_NULL_HANDLE; VkBuffer ibuf = VK_NULL_HANDLE; - if (lastVType_ & GE_VTYPE_WEIGHT_MASK) { + if (g_Config.bSoftwareSkinning && (lastVType_ & GE_VTYPE_WEIGHT_MASK)) { useCache = false; } @@ -730,7 +743,7 @@ void DrawEngineVulkan::DoFlush() { break; } } else { - if (lastVType_ & GE_VTYPE_WEIGHT_MASK) { + if (g_Config.bSoftwareSkinning && (lastVType_ & GE_VTYPE_WEIGHT_MASK)) { // If software skinning, we've already predecoded into "decoded". So push that content. VkDeviceSize size = decodedVerts_ * dec_->GetDecVtxFmt().stride; u8 *dest = (u8 *)frame->pushVertex->Push(size, &vbOffset, &vbuf); @@ -802,12 +815,12 @@ void DrawEngineVulkan::DoFlush() { dirtyUniforms_ |= shaderManager_->UpdateUniforms(); UpdateUBOs(frame); - VkDescriptorSet ds = GetOrCreateDescriptorSet(imageView, sampler, baseBuf, lightBuf, tess); + VkDescriptorSet ds = GetOrCreateDescriptorSet(imageView, sampler, baseBuf, lightBuf, boneBuf, tess); { PROFILE_THIS_SCOPE("renderman_q"); - const uint32_t dynamicUBOOffsets[2] = { - baseUBOOffset, lightUBOOffset, + const uint32_t dynamicUBOOffsets[3] = { + baseUBOOffset, lightUBOOffset, boneUBOOffset, }; int stride = dec_->GetDecVtxFmt().stride; @@ -908,9 +921,9 @@ void DrawEngineVulkan::DoFlush() { // Even if the first draw is through-mode, make sure we at least have one copy of these uniforms buffered UpdateUBOs(frame); - VkDescriptorSet ds = GetOrCreateDescriptorSet(imageView, sampler, baseBuf, lightBuf, tess); - const uint32_t dynamicUBOOffsets[2] = { - baseUBOOffset, lightUBOOffset, + VkDescriptorSet ds = GetOrCreateDescriptorSet(imageView, sampler, baseBuf, lightBuf, boneBuf, tess); + const uint32_t dynamicUBOOffsets[3] = { + baseUBOOffset, lightUBOOffset, boneUBOOffset, }; PROFILE_THIS_SCOPE("renderman_q"); @@ -977,6 +990,10 @@ void DrawEngineVulkan::UpdateUBOs(FrameData *frame) { lightUBOOffset = shaderManager_->PushLightBuffer(frame->pushUBO, &lightBuf); dirtyUniforms_ &= ~DIRTY_LIGHT_UNIFORMS; } + if ((dirtyUniforms_ & DIRTY_BONE_UNIFORMS) || boneBuf == VK_NULL_HANDLE) { + boneUBOOffset = shaderManager_->PushBoneBuffer(frame->pushUBO, &boneBuf); + dirtyUniforms_ &= ~DIRTY_BONE_UNIFORMS; + } } DrawEngineVulkan::TessellationDataTransferVulkan::TessellationDataTransferVulkan(VulkanContext *vulkan, Draw::DrawContext *draw) diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index 1cc4976bc3..74cf5a0655 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -23,7 +23,7 @@ // * binding 1: Secondary texture sampler for shader blending or depal palettes // * binding 2: Base Uniform Buffer (includes fragment state) // * binding 3: Light uniform buffer -// * binding 4: Shader buffer storage for tesselation +// * binding 4: Bone uniform buffer // // All shaders conform to this layout, so they are all compatible with the same descriptor set. // The format of the various uniform buffers may vary though - vertex shaders that don't skin @@ -194,7 +194,7 @@ private: void DoFlush(); void UpdateUBOs(FrameData *frame); - VkDescriptorSet GetOrCreateDescriptorSet(VkImageView imageView, VkSampler sampler, VkBuffer base, VkBuffer light, bool tess); + VkDescriptorSet GetOrCreateDescriptorSet(VkImageView imageView, VkSampler sampler, VkBuffer base, VkBuffer light, VkBuffer bone, bool tess); VulkanContext *vulkan_; Draw::DrawContext *draw_; @@ -218,7 +218,7 @@ private: VkImageView imageView_; VkImageView secondaryImageView_; VkSampler sampler_; - VkBuffer base_, light_; // All three UBO slots will be set to this. This will usually be identical + VkBuffer base_, light_, bone_; // All three UBO slots will be set to this. This will usually be identical // for all draws in a frame, except when the buffer has to grow. }; @@ -252,7 +252,8 @@ private: uint64_t dirtyUniforms_; uint32_t baseUBOOffset; uint32_t lightUBOOffset; - VkBuffer baseBuf, lightBuf; + uint32_t boneUBOOffset; + VkBuffer baseBuf, lightBuf, boneBuf; VkImageView imageView = VK_NULL_HANDLE; VkSampler sampler = VK_NULL_HANDLE; diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 621b7f6f7f..b5d78c9595 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -252,6 +252,7 @@ void GPU_Vulkan::CheckGPUFeatures() { void GPU_Vulkan::BeginHostFrame() { drawEngine_.BeginFrame(); + UpdateCmdInfo(); if (resized_) { CheckGPUFeatures(); @@ -490,6 +491,7 @@ void GPU_Vulkan::DeviceRestore() { CheckGPUFeatures(); BuildReportingInfo(); + UpdateCmdInfo(); framebufferManagerVulkan_->DeviceRestore(vulkan_, draw_); vulkan2D_.DeviceRestore(vulkan_); diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index bf2e425b7f..17106e0951 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -158,9 +158,11 @@ ShaderManagerVulkan::ShaderManagerVulkan(VulkanContext *vulkan) uboAlignment_ = vulkan_->GetPhysicalDeviceProperties().limits.minUniformBufferOffsetAlignment; memset(&ub_base, 0, sizeof(ub_base)); memset(&ub_lights, 0, sizeof(ub_lights)); + memset(&ub_bones, 0, sizeof(ub_bones)); ILOG("sizeof(ub_base): %d", (int)sizeof(ub_base)); ILOG("sizeof(ub_lights): %d", (int)sizeof(ub_lights)); + ILOG("sizeof(ub_bones): %d", (int)sizeof(ub_bones)); } ShaderManagerVulkan::~ShaderManagerVulkan() { @@ -213,6 +215,8 @@ uint64_t ShaderManagerVulkan::UpdateUniforms() { BaseUpdateUniforms(&ub_base, dirty, false); if (dirty & DIRTY_LIGHT_UNIFORMS) LightUpdateUniforms(&ub_lights, dirty); + if (dirty & DIRTY_BONE_UNIFORMS) + BoneUpdateUniforms(&ub_bones, dirty); } gstate_c.CleanUniforms(); return dirty; diff --git a/GPU/Vulkan/ShaderManagerVulkan.h b/GPU/Vulkan/ShaderManagerVulkan.h index 281d4768e0..2bcce9332b 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.h +++ b/GPU/Vulkan/ShaderManagerVulkan.h @@ -111,6 +111,7 @@ public: // Applies dirty changes and copies the buffer. bool IsBaseDirty() { return true; } bool IsLightDirty() { return true; } + bool IsBoneDirty() { return true; } uint32_t PushBaseBuffer(VulkanPushBuffer *dest, VkBuffer *buf) { return dest->PushAligned(&ub_base, sizeof(ub_base), uboAlignment_, buf); @@ -118,6 +119,10 @@ public: uint32_t PushLightBuffer(VulkanPushBuffer *dest, VkBuffer *buf) { return dest->PushAligned(&ub_lights, sizeof(ub_lights), uboAlignment_, buf); } + // TODO: Only push half the bone buffer if we only have four bones. + uint32_t PushBoneBuffer(VulkanPushBuffer *dest, VkBuffer *buf) { + return dest->PushAligned(&ub_bones, sizeof(ub_bones), uboAlignment_, buf); + } bool LoadCache(FILE *f); void SaveCache(FILE *f); @@ -139,6 +144,7 @@ private: // Uniform block scratchpad. These (the relevant ones) are copied to the current pushbuffer at draw time. UB_VS_FS_Base ub_base; UB_VS_Lights ub_lights; + UB_VS_Bones ub_bones; VulkanFragmentShader *lastFShader_; VulkanVertexShader *lastVShader_; diff --git a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp index 0b39ef5085..0dfcf57d11 100644 --- a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp @@ -54,6 +54,18 @@ static const char *vulkan_glsl_preamble = #define WRITE p+=sprintf +static const char * const boneWeightDecl[9] = { + "#ERROR#", + "layout(location = 3) in float w1;\n", + "layout(location = 3) in vec2 w1;\n", + "layout(location = 3) in vec3 w1;\n", + "layout(location = 3) in vec4 w1;\n", + "layout(location = 3) in vec4 w1;\nlayout(location = 4) in float w2;\n", + "layout(location = 3) in vec4 w1;\nlayout(location = 4) in vec2 w2;\n", + "layout(location = 3) in vec4 w1;\nlayout(location = 4) in vec3 w2;\n", + "layout(location = 3) in vec4 w1;\nlayout(location = 4) in vec4 w2;\n", +}; + enum DoLightComputation { LIGHT_OFF, LIGHT_SHADE, @@ -114,6 +126,7 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) { bool flipNormal = id.Bit(VS_BIT_NORM_REVERSE); int ls0 = id.Bits(VS_BIT_LS0, 2); int ls1 = id.Bits(VS_BIT_LS1, 2); + bool enableBones = id.Bit(VS_BIT_ENABLE_BONES); bool enableLighting = id.Bit(VS_BIT_LIGHTING_ENABLE); int matUpdate = id.Bits(VS_BIT_MATERIAL_UPDATE, 3); @@ -127,6 +140,8 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) { WRITE(p, "layout (std140, set = 0, binding = 2) uniform baseVars {\n%s} base;\n", ub_baseStr); if (enableLighting || doShadeMapping) WRITE(p, "layout (std140, set = 0, binding = 3) uniform lightVars {\n%s} light;\n", ub_vs_lightsStr); + if (enableBones) + WRITE(p, "layout (std140, set = 0, binding = 4) uniform boneVars {\n%s} bone;\n", ub_vs_bonesStr); const char *shading = doFlatShading ? "flat " : ""; @@ -142,6 +157,13 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) { } } + int numBoneWeights = 0; + int boneWeightScale = id.Bits(VS_BIT_WEIGHT_FMTSCALE, 2); + if (enableBones) { + numBoneWeights = 1 + id.Bits(VS_BIT_BONES, 3); + WRITE(p, "%s", boneWeightDecl[numBoneWeights]); + } + if (useHWTransform) WRITE(p, "layout (location = %d) in vec3 position;\n", (int)PspAttributeLocation::POSITION); else @@ -307,7 +329,7 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) { } } else { // Step 1: World Transform / Skinning - if (true) { + if (!enableBones) { if (doBezier || doSpline) { WRITE(p, " vec3 _pos[16];\n"); WRITE(p, " vec2 _tex[16];\n"); @@ -413,6 +435,34 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) { else WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n"); } + } else { + static const char *rescale[4] = { "", " * 1.9921875", " * 1.999969482421875", "" }; // 2*127.5f/128.f, 2*32767.5f/32768.f, 1.0f}; + const char *factor = rescale[boneWeightScale]; + + static const char * const boneWeightAttr[8] = { + "w1.x", "w1.y", "w1.z", "w1.w", + "w2.x", "w2.y", "w2.z", "w2.w", + }; + + WRITE(p, " mat3x4 skinMatrix = w1.x * bone.m[0];\n"); + if (numBoneWeights > 1) { + for (int i = 1; i < numBoneWeights; i++) { + WRITE(p, " skinMatrix += %s * bone.m[%i];\n", boneWeightAttr[i], i); + } + } + + WRITE(p, ";\n"); + + // Trying to simplify this results in bugs in LBP... + WRITE(p, " vec3 skinnedpos = (vec4(position, 1.0) * skinMatrix) %s;\n", factor); + WRITE(p, " vec3 worldpos = vec4(skinnedpos, 1.0) * base.world_mtx;\n"); + + if (hasNormal) { + WRITE(p, " mediump vec3 skinnednormal = vec4(%snormal, 0.0) * skinMatrix %s;\n", flipNormal ? "-" : "", factor); + } else { + WRITE(p, " mediump vec3 skinnednormal = vec4(0.0, 0.0, %s1.0, 0.0) * skinMatrix %s;\n", flipNormal ? "-" : "", factor); + } + WRITE(p, " mediump vec3 worldnormal = normalize(vec4(skinnednormal, 0.0) * base.world_mtx);\n"); } WRITE(p, " vec4 viewPos = vec4(vec4(worldpos, 1.0) * base.view_mtx, 1.0);\n"); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 7b485d4b68..904477697e 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -312,6 +312,13 @@ void GameSettingsScreen::CreateViews() { hwTransform->OnClick.Handle(this, &GameSettingsScreen::OnHardwareTransform); hwTransform->SetDisabledPtr(&g_Config.bSoftwareRendering); + CheckBox *swSkin = graphicsSettings->Add(new CheckBox(&g_Config.bSoftwareSkinning, gr->T("Software Skinning"))); + swSkin->OnClick.Add([=](EventParams &e) { + settingInfo_->Show(gr->T("SoftwareSkinning Tip", "Combine skinned model draws on the CPU, faster in most games"), e.v); + return UI::EVENT_CONTINUE; + }); + swSkin->SetDisabledPtr(&g_Config.bSoftwareRendering); + CheckBox *vtxCache = graphicsSettings->Add(new CheckBox(&g_Config.bVertexCache, gr->T("Vertex Cache"))); vtxCache->OnClick.Add([=](EventParams &e) { settingInfo_->Show(gr->T("VertexCache Tip", "Faster, but may cause temporary flicker"), e.v); diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index e9ad43fad7..0dd3ba8597 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -41,7 +41,7 @@ struct VkRenderData { VkPipelineLayout pipelineLayout; VkDescriptorSet ds; int numUboOffsets; - uint32_t uboOffsets[2]; + uint32_t uboOffsets[3]; VkBuffer vbuffer; // might need to increase at some point VkDeviceSize voffset; VkBuffer ibuffer; diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 883f608537..661211d1d0 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -369,6 +369,7 @@ int main(int argc, const char* argv[]) g_Config.bFrameSkipUnthrottle = false; g_Config.bEnableLogging = fullLog; g_Config.iNumWorkerThreads = 1; + g_Config.bSoftwareSkinning = true; g_Config.bVertexDecoderJit = true; g_Config.bBlockTransferGPU = true; g_Config.iSplineBezierQuality = 2; diff --git a/unittest/TestVertexJit.cpp b/unittest/TestVertexJit.cpp index dc964c4564..40dd640c8b 100644 --- a/unittest/TestVertexJit.cpp +++ b/unittest/TestVertexJit.cpp @@ -543,6 +543,7 @@ static bool TestVertexColor565() { static bool TestVertex8Skin() { VertexDecoderTestHarness dec; + g_Config.bSoftwareSkinning = true; for (int i = 0; i < 8 * 12; ++i) { gstate.boneMatrix[i] = 0.0f; } @@ -572,6 +573,7 @@ static bool TestVertex8Skin() { static bool TestVertex16Skin() { VertexDecoderTestHarness dec; + g_Config.bSoftwareSkinning = true; for (int i = 0; i < 8 * 12; ++i) { gstate.boneMatrix[i] = 0.0f; } @@ -601,6 +603,7 @@ static bool TestVertex16Skin() { static bool TestVertexFloatSkin() { VertexDecoderTestHarness dec; + g_Config.bSoftwareSkinning = true; for (int i = 0; i < 8 * 12; ++i) { gstate.boneMatrix[i] = 0.0f; } From 0345e1a7d4b278ade169501ea90385ff432d09e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Apr 2018 12:28:30 +0200 Subject: [PATCH 16/40] Revert "Oops, fix hardware tess in Vulkan (after removing hw skinning, we need to adjust the binding number)" This reverts commit 87575910df4ca68299b3391526885317fe13fd3b. --- GPU/Vulkan/VertexShaderGeneratorVulkan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp index 0dfcf57d11..d7517cba6a 100644 --- a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp @@ -221,7 +221,7 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) { WRITE(p, " vec4 uv;\n"); WRITE(p, " vec4 color;\n"); WRITE(p, "};"); - WRITE(p, "layout (std430, set = 0, binding = 4) buffer s_tess_data {\n"); + WRITE(p, "layout (std430, set = 0, binding = 5) buffer s_tess_data {\n"); WRITE(p, " TessData data[];"); WRITE(p, "} tess_data;"); From ccd594dae792fcbf4937ecf86f4815d29fa362c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Apr 2018 12:30:49 +0200 Subject: [PATCH 17/40] Revert "VertexLoader: Remove now-unused weights translation code" This reverts commit 44100c6c1d38bf2318b157cd28a1cbf7aab5270d. --- GPU/Common/VertexDecoderArm.cpp | 52 +++++++++ GPU/Common/VertexDecoderArm64.cpp | 41 +++++++ GPU/Common/VertexDecoderCommon.cpp | 75 +++++++++++++ GPU/Common/VertexDecoderCommon.h | 12 ++ GPU/Common/VertexDecoderX86.cpp | 175 +++++++++++++++++++++++++++++ 5 files changed, 355 insertions(+) diff --git a/GPU/Common/VertexDecoderArm.cpp b/GPU/Common/VertexDecoderArm.cpp index 79e6584ae2..1f7d65a833 100644 --- a/GPU/Common/VertexDecoderArm.cpp +++ b/GPU/Common/VertexDecoderArm.cpp @@ -111,6 +111,9 @@ static const ARMReg srcNEON = Q2; static const ARMReg accNEON = Q3; static const JitLookup jitLookup[] = { + {&VertexDecoder::Step_WeightsU8, &VertexDecoderJitCache::Jit_WeightsU8}, + {&VertexDecoder::Step_WeightsU16, &VertexDecoderJitCache::Jit_WeightsU16}, + {&VertexDecoder::Step_WeightsFloat, &VertexDecoderJitCache::Jit_WeightsFloat}, {&VertexDecoder::Step_WeightsU8Skin, &VertexDecoderJitCache::Jit_WeightsU8Skin}, {&VertexDecoder::Step_WeightsU16Skin, &VertexDecoderJitCache::Jit_WeightsU16Skin}, {&VertexDecoder::Step_WeightsFloatSkin, &VertexDecoderJitCache::Jit_WeightsFloatSkin}, @@ -323,6 +326,55 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int return (JittedVertexDecoder)start; } +void VertexDecoderJitCache::Jit_WeightsU8() { + // Basic implementation - a byte at a time. TODO: Optimize + int j; + for (j = 0; j < dec_->nweights; j++) { + LDRB(tempReg1, srcReg, dec_->weightoff + j); + STRB(tempReg1, dstReg, dec_->decFmt.w0off + j); + } + if (j & 3) { + // Create a zero register. Might want to make a fixed one. + EOR(scratchReg, scratchReg, scratchReg); + } + while (j & 3) { + STRB(scratchReg, dstReg, dec_->decFmt.w0off + j); + j++; + } +} + +void VertexDecoderJitCache::Jit_WeightsU16() { + // Basic implementation - a short at a time. TODO: Optimize + int j; + for (j = 0; j < dec_->nweights; j++) { + LDRH(tempReg1, srcReg, dec_->weightoff + j * 2); + STRH(tempReg1, dstReg, dec_->decFmt.w0off + j * 2); + } + if (j & 3) { + // Create a zero register. Might want to make a fixed one. + EOR(scratchReg, scratchReg, scratchReg); + } + while (j & 3) { + STRH(scratchReg, dstReg, dec_->decFmt.w0off + j * 2); + j++; + } +} + +void VertexDecoderJitCache::Jit_WeightsFloat() { + int j; + for (j = 0; j < dec_->nweights; j++) { + LDR(tempReg1, srcReg, dec_->weightoff + j * 4); + STR(tempReg1, dstReg, dec_->decFmt.w0off + j * 4); + } + if (j & 3) { + EOR(tempReg1, tempReg1, tempReg1); + } + while (j & 3) { // Zero additional weights rounding up to 4. + STR(tempReg1, dstReg, dec_->decFmt.w0off + j * 4); + j++; + } +} + static const ARMReg weightRegs[8] = { S8, S9, S10, S11, S12, S13, S14, S15 }; static const ARMReg neonWeightRegsD[4] = { D4, D5, D6, D7 }; static const ARMReg neonWeightRegsQ[2] = { Q2, Q3 }; diff --git a/GPU/Common/VertexDecoderArm64.cpp b/GPU/Common/VertexDecoderArm64.cpp index acfa230ea9..aab6b4ca36 100644 --- a/GPU/Common/VertexDecoderArm64.cpp +++ b/GPU/Common/VertexDecoderArm64.cpp @@ -85,6 +85,9 @@ static const ARM64Reg neonWeightRegsQ[2] = { Q3, Q2 }; // reverse order to prev // Q16+ are free-for-all for matrices. In 16 registers, we can fit 4 4x4 matrices. static const JitLookup jitLookup[] = { + {&VertexDecoder::Step_WeightsU8, &VertexDecoderJitCache::Jit_WeightsU8}, + {&VertexDecoder::Step_WeightsU16, &VertexDecoderJitCache::Jit_WeightsU16}, + {&VertexDecoder::Step_WeightsFloat, &VertexDecoderJitCache::Jit_WeightsFloat}, {&VertexDecoder::Step_WeightsU8Skin, &VertexDecoderJitCache::Jit_WeightsU8Skin}, {&VertexDecoder::Step_WeightsU16Skin, &VertexDecoderJitCache::Jit_WeightsU16Skin}, {&VertexDecoder::Step_WeightsFloatSkin, &VertexDecoderJitCache::Jit_WeightsFloatSkin}, @@ -353,6 +356,44 @@ void VertexDecoderJitCache::Jit_ApplyWeights() { } } +void VertexDecoderJitCache::Jit_WeightsU8() { + // Basic implementation - a byte at a time. TODO: Optimize + int j; + for (j = 0; j < dec_->nweights; j++) { + LDRB(INDEX_UNSIGNED, tempReg1, srcReg, dec_->weightoff + j); + STRB(INDEX_UNSIGNED, tempReg1, dstReg, dec_->decFmt.w0off + j); + } + while (j & 3) { + STRB(INDEX_UNSIGNED, WZR, dstReg, dec_->decFmt.w0off + j); + j++; + } +} + +void VertexDecoderJitCache::Jit_WeightsU16() { + // Basic implementation - a short at a time. TODO: Optimize + int j; + for (j = 0; j < dec_->nweights; j++) { + LDRH(INDEX_UNSIGNED, tempReg1, srcReg, dec_->weightoff + j * 2); + STRH(INDEX_UNSIGNED, tempReg1, dstReg, dec_->decFmt.w0off + j * 2); + } + while (j & 3) { + STRH(INDEX_UNSIGNED, WZR, dstReg, dec_->decFmt.w0off + j * 2); + j++; + } +} + +void VertexDecoderJitCache::Jit_WeightsFloat() { + int j; + for (j = 0; j < dec_->nweights; j++) { + LDR(INDEX_UNSIGNED, tempReg1, srcReg, dec_->weightoff + j * 4); + STR(INDEX_UNSIGNED, tempReg1, dstReg, dec_->decFmt.w0off + j * 4); + } + while (j & 3) { // Zero additional weights rounding up to 4. + STR(INDEX_UNSIGNED, WZR, dstReg, dec_->decFmt.w0off + j * 4); + j++; + } +} + void VertexDecoderJitCache::Jit_WeightsU8Skin() { // Weight is first so srcReg is correct. switch (dec_->nweights) { diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index 6c517c5065..fc823ff62c 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -177,6 +177,67 @@ void PrintDecodedVertex(VertexReader &vtx) { VertexDecoder::VertexDecoder() : decoded_(nullptr), ptr_(nullptr), jitted_(0), jittedSize_(0) { } +void VertexDecoder::Step_WeightsU8() const +{ + u8 *wt = (u8 *)(decoded_ + decFmt.w0off); + const u8 *wdata = (const u8*)(ptr_); + int j; + for (j = 0; j < nweights; j++) + wt[j] = wdata[j]; + while (j & 3) // Zero additional weights rounding up to 4. + wt[j++] = 0; +} + +void VertexDecoder::Step_WeightsU16() const +{ + u16 *wt = (u16 *)(decoded_ + decFmt.w0off); + const u16 *wdata = (const u16*)(ptr_); + int j; + for (j = 0; j < nweights; j++) + wt[j] = wdata[j]; + while (j & 3) // Zero additional weights rounding up to 4. + wt[j++] = 0; +} + +void VertexDecoder::Step_WeightsU8ToFloat() const +{ + float *wt = (float *)(decoded_ + decFmt.w0off); + const u8 *wdata = (const u8*)(ptr_); + int j; + for (j = 0; j < nweights; j++) { + wt[j] = (float)wdata[j] * (1.0f / 128.0f); + } + while (j & 3) // Zero additional weights rounding up to 4. + wt[j++] = 0; +} + +void VertexDecoder::Step_WeightsU16ToFloat() const +{ + float *wt = (float *)(decoded_ + decFmt.w0off); + const u16 *wdata = (const u16*)(ptr_); + int j; + for (j = 0; j < nweights; j++) { + wt[j] = (float)wdata[j] * (1.0f / 32768.0f); + } + while (j & 3) // Zero additional weights rounding up to 4. + wt[j++] = 0; +} + +// Float weights should be uncommon, we can live with having to multiply these by 2.0 +// to avoid special checks in the vertex shader generator. +// (PSP uses 0.0-2.0 fixed point numbers for weights) +void VertexDecoder::Step_WeightsFloat() const +{ + float *wt = (float *)(decoded_ + decFmt.w0off); + const float *wdata = (const float*)(ptr_); + int j; + for (j = 0; j < nweights; j++) { + wt[j] = wdata[j]; + } + while (j & 3) // Zero additional weights rounding up to 4. + wt[j++] = 0.0f; +} + void VertexDecoder::ComputeSkinMatrix(const float weights[8]) const { memset(skinMatrix, 0, sizeof(skinMatrix)); for (int j = 0; j < nweights; j++) { @@ -809,6 +870,20 @@ void VertexDecoder::Step_PosFloatMorphSkin() const { Vec3ByMatrix43(v, pos, skinMatrix); } +static const StepFunction wtstep[4] = { + 0, + &VertexDecoder::Step_WeightsU8, + &VertexDecoder::Step_WeightsU16, + &VertexDecoder::Step_WeightsFloat, +}; + +static const StepFunction wtstepToFloat[4] = { + 0, + &VertexDecoder::Step_WeightsU8ToFloat, + &VertexDecoder::Step_WeightsU16ToFloat, + &VertexDecoder::Step_WeightsFloat, +}; + // TODO: Morph weights correctly! This is missing. Not sure if any game actually // use this functionality at all. diff --git a/GPU/Common/VertexDecoderCommon.h b/GPU/Common/VertexDecoderCommon.h index 53173dd319..97ea633c1e 100644 --- a/GPU/Common/VertexDecoderCommon.h +++ b/GPU/Common/VertexDecoderCommon.h @@ -461,6 +461,12 @@ public: std::string GetString(DebugShaderStringType stringType); + void Step_WeightsU8() const; + void Step_WeightsU16() const; + void Step_WeightsU8ToFloat() const; + void Step_WeightsU16ToFloat() const; + void Step_WeightsFloat() const; + void ComputeSkinMatrix(const float weights[8]) const; void Step_WeightsU8Skin() const; @@ -613,6 +619,12 @@ public: JittedVertexDecoder Compile(const VertexDecoder &dec, int32_t *jittedSize); void Clear(); + void Jit_WeightsU8(); + void Jit_WeightsU16(); + void Jit_WeightsU8ToFloat(); + void Jit_WeightsU16ToFloat(); + void Jit_WeightsFloat(); + void Jit_WeightsU8Skin(); void Jit_WeightsU16Skin(); void Jit_WeightsFloatSkin(); diff --git a/GPU/Common/VertexDecoderX86.cpp b/GPU/Common/VertexDecoderX86.cpp index 3b6b22ae16..6961291a3f 100644 --- a/GPU/Common/VertexDecoderX86.cpp +++ b/GPU/Common/VertexDecoderX86.cpp @@ -94,10 +94,16 @@ static const X64Reg fpScratchReg4 = XMM4; // on the interpreter if the compiler fails. static const JitLookup jitLookup[] = { + {&VertexDecoder::Step_WeightsU8, &VertexDecoderJitCache::Jit_WeightsU8}, + {&VertexDecoder::Step_WeightsU16, &VertexDecoderJitCache::Jit_WeightsU16}, + {&VertexDecoder::Step_WeightsFloat, &VertexDecoderJitCache::Jit_WeightsFloat}, {&VertexDecoder::Step_WeightsU8Skin, &VertexDecoderJitCache::Jit_WeightsU8Skin}, {&VertexDecoder::Step_WeightsU16Skin, &VertexDecoderJitCache::Jit_WeightsU16Skin}, {&VertexDecoder::Step_WeightsFloatSkin, &VertexDecoderJitCache::Jit_WeightsFloatSkin}, + {&VertexDecoder::Step_WeightsU8ToFloat, &VertexDecoderJitCache::Jit_WeightsU8ToFloat}, + {&VertexDecoder::Step_WeightsU16ToFloat, &VertexDecoderJitCache::Jit_WeightsU16ToFloat}, + {&VertexDecoder::Step_TcFloat, &VertexDecoderJitCache::Jit_TcFloat}, {&VertexDecoder::Step_TcU8ToFloat, &VertexDecoderJitCache::Jit_TcU8ToFloat}, {&VertexDecoder::Step_TcU16ToFloat, &VertexDecoderJitCache::Jit_TcU16ToFloat}, @@ -276,6 +282,175 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int return (JittedVertexDecoder)start; } +void VertexDecoderJitCache::Jit_WeightsU8() { + switch (dec_->nweights) { + case 1: + MOVZX(32, 8, tempReg1, MDisp(srcReg, dec_->weightoff)); + break; + case 2: + MOVZX(32, 16, tempReg1, MDisp(srcReg, dec_->weightoff)); + break; + case 3: + MOV(32, R(tempReg1), MDisp(srcReg, dec_->weightoff)); + AND(32, R(tempReg1), Imm32(0x00FFFFFF)); + break; + case 4: + MOV(32, R(tempReg1), MDisp(srcReg, dec_->weightoff)); + break; + case 5: + MOV(32, R(tempReg1), MDisp(srcReg, dec_->weightoff)); + MOVZX(32, 8, tempReg2, MDisp(srcReg, dec_->weightoff + 4)); + break; + case 6: + MOV(32, R(tempReg1), MDisp(srcReg, dec_->weightoff)); + MOVZX(32, 16, tempReg2, MDisp(srcReg, dec_->weightoff + 4)); + break; + case 7: + MOV(32, R(tempReg1), MDisp(srcReg, dec_->weightoff)); + MOV(32, R(tempReg2), MDisp(srcReg, dec_->weightoff + 4)); + AND(32, R(tempReg2), Imm32(0x00FFFFFF)); + break; + case 8: + MOV(32, R(tempReg1), MDisp(srcReg, dec_->weightoff)); + MOV(32, R(tempReg2), MDisp(srcReg, dec_->weightoff + 4)); + break; + } + + if (dec_->nweights <= 4) { + MOV(32, MDisp(dstReg, dec_->decFmt.w0off), R(tempReg1)); + } else { + MOV(32, MDisp(dstReg, dec_->decFmt.w0off), R(tempReg1)); + MOV(32, MDisp(dstReg, dec_->decFmt.w1off), R(tempReg2)); + } +} + +void VertexDecoderJitCache::Jit_WeightsU16() { + switch (dec_->nweights) { + case 1: + MOVZX(32, 16, tempReg1, MDisp(srcReg, dec_->weightoff)); + MOV(32, MDisp(dstReg, dec_->decFmt.w0off), R(tempReg1)); + MOV(32, MDisp(dstReg, dec_->decFmt.w0off + 4), Imm32(0)); + return; + + case 2: + MOV(32, R(tempReg1), MDisp(srcReg, dec_->weightoff)); + MOV(32, MDisp(dstReg, dec_->decFmt.w0off), R(tempReg1)); + MOV(32, MDisp(dstReg, dec_->decFmt.w0off + 4), Imm32(0)); + return; + + case 3: + MOV(32, R(tempReg1), MDisp(srcReg, dec_->weightoff)); + MOVZX(32, 16, tempReg2, MDisp(srcReg, dec_->weightoff + 4)); + MOV(32, MDisp(dstReg, dec_->decFmt.w0off), R(tempReg1)); + MOV(32, MDisp(dstReg, dec_->decFmt.w0off + 4), R(tempReg2)); + return; + + case 4: + // Anything above 4 will do 4 here, and then the rest after. + case 5: + case 6: + case 7: + case 8: + MOV(32, R(tempReg1), MDisp(srcReg, dec_->weightoff)); + MOV(32, R(tempReg2), MDisp(srcReg, dec_->weightoff + 4)); + MOV(32, MDisp(dstReg, dec_->decFmt.w0off), R(tempReg1)); + MOV(32, MDisp(dstReg, dec_->decFmt.w0off + 4), R(tempReg2)); + break; + } + + // Basic implementation - a short at a time. TODO: Optimize + int j; + for (j = 4; j < dec_->nweights; j++) { + MOV(16, R(tempReg1), MDisp(srcReg, dec_->weightoff + j * 2)); + MOV(16, MDisp(dstReg, dec_->decFmt.w0off + j * 2), R(tempReg1)); + } + while (j & 3) { + MOV(16, MDisp(dstReg, dec_->decFmt.w0off + j * 2), Imm16(0)); + j++; + } +} + +void VertexDecoderJitCache::Jit_WeightsU8ToFloat() { + if (dec_->nweights >= 4) { + Jit_AnyU8ToFloat(dec_->weightoff, 32); + MOVUPS(MDisp(dstReg, dec_->decFmt.w0off), XMM3); + if (dec_->nweights > 4) { + Jit_AnyU8ToFloat(dec_->weightoff + 4, (dec_->nweights - 4) * 8); + MOVUPS(MDisp(dstReg, dec_->decFmt.w1off), XMM3); + } + } else { + Jit_AnyU8ToFloat(dec_->weightoff, dec_->nweights * 8); + MOVUPS(MDisp(dstReg, dec_->decFmt.w0off), XMM3); + } +} + +void VertexDecoderJitCache::Jit_WeightsU16ToFloat() { + if (dec_->nweights >= 4) { + Jit_AnyU16ToFloat(dec_->weightoff, 64); + MOVUPS(MDisp(dstReg, dec_->decFmt.w0off), XMM3); + if (dec_->nweights > 4) { + Jit_AnyU16ToFloat(dec_->weightoff + 4 * 2, (dec_->nweights - 4) * 16); + MOVUPS(MDisp(dstReg, dec_->decFmt.w1off), XMM3); + } + } else { + Jit_AnyU16ToFloat(dec_->weightoff, dec_->nweights * 16); + MOVUPS(MDisp(dstReg, dec_->decFmt.w0off), XMM3); + } +} + +void VertexDecoderJitCache::Jit_WeightsFloat() { + int j; + switch (dec_->nweights) { + case 1: + // MOVSS: When the source operand is a memory location and destination operand is an XMM register, the three high-order doublewords of the destination operand are cleared to all 0s. + MOVSS(XMM3, MDisp(srcReg, dec_->weightoff)); + MOVUPS(MDisp(dstReg, dec_->decFmt.w0off), XMM3); + break; + + case 2: + MOVQ_xmm(XMM3, MDisp(srcReg, dec_->weightoff)); + MOVUPS(MDisp(dstReg, dec_->decFmt.w0off), XMM3); + break; + + case 4: + MOVUPS(XMM3, MDisp(srcReg, dec_->weightoff)); + MOVUPS(MDisp(dstReg, dec_->decFmt.w0off), XMM3); + break; + + case 5: + MOVUPS(XMM3, MDisp(srcReg, dec_->weightoff)); + MOVSS(XMM4, MDisp(srcReg, dec_->weightoff + 16)); + MOVUPS(MDisp(dstReg, dec_->decFmt.w0off), XMM3); + MOVUPS(MDisp(dstReg, dec_->decFmt.w0off + 16), XMM4); + break; + + case 6: + MOVUPS(XMM3, MDisp(srcReg, dec_->weightoff)); + MOVQ_xmm(XMM4, MDisp(srcReg, dec_->weightoff + 16)); + MOVUPS(MDisp(dstReg, dec_->decFmt.w0off), XMM3); + MOVUPS(MDisp(dstReg, dec_->decFmt.w0off + 16), XMM4); + break; + + case 8: + MOVUPS(XMM3, MDisp(srcReg, dec_->weightoff)); + MOVUPS(XMM4, MDisp(srcReg, dec_->weightoff + 16)); + MOVUPS(MDisp(dstReg, dec_->decFmt.w0off), XMM3); + MOVUPS(MDisp(dstReg, dec_->decFmt.w0off + 16), XMM4); + break; + + default: + for (j = 0; j < dec_->nweights; j++) { + MOV(32, R(tempReg1), MDisp(srcReg, dec_->weightoff + j * 4)); + MOV(32, MDisp(dstReg, dec_->decFmt.w0off + j * 4), R(tempReg1)); + } + while (j & 3) { // Zero additional weights rounding up to 4. + MOV(32, MDisp(dstReg, dec_->decFmt.w0off + j * 4), Imm32(0)); + j++; + } + break; + } +} + void VertexDecoderJitCache::Jit_WeightsU8Skin() { MOV(PTRBITS, R(tempReg2), ImmPtr(&bones)); From 00f3bf6102285993f0a22994ef886793b856b3ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Apr 2018 12:31:21 +0200 Subject: [PATCH 18/40] Revert "Start removing support for hardware skinning." This reverts commit dcb773d62436525b4719555ce23a38a1342b45f7. --- GPU/Common/VertexDecoderCommon.cpp | 48 +++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index fc823ff62c..97d8ced338 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -1082,7 +1082,7 @@ void VertexDecoder::SetVertexType(u32 fmt, const VertexDecoderOptions &options, DEBUG_LOG(G3D, "VTYPE: THRU=%i TC=%i COL=%i POS=%i NRM=%i WT=%i NW=%i IDX=%i MC=%i", (int)throughmode, tc, col, pos, nrm, weighttype, nweights, idx, morphcount); } - bool skinning = weighttype != 0; + bool skinInDecode = weighttype != 0 && g_Config.bSoftwareSkinning && morphcount == 1; if (weighttype) { // && nweights? weightoff = size; @@ -1091,11 +1091,43 @@ void VertexDecoder::SetVertexType(u32 fmt, const VertexDecoderOptions &options, if (wtalign[weighttype] > biggest) biggest = wtalign[weighttype]; - // No visible output, computes a matrix that is passed through the skinMatrix variable - // to the "nrm" and "pos" steps. - // Technically we should support morphing the weights too, but I have a hard time - // imagining that any game would use that.. but you never know. - steps_[numSteps_++] = wtstep_skin[weighttype]; + if (skinInDecode) { + // No visible output, computes a matrix that is passed through the skinMatrix variable + // to the "nrm" and "pos" steps. + // Technically we should support morphing the weights too, but I have a hard time + // imagining that any game would use that.. but you never know. + steps_[numSteps_++] = wtstep_skin[weighttype]; + } else { + int fmtBase = DEC_FLOAT_1; + if (options.expandAllWeightsToFloat) { + steps_[numSteps_++] = wtstepToFloat[weighttype]; + fmtBase = DEC_FLOAT_1; + } else { + steps_[numSteps_++] = wtstep[weighttype]; + if (weighttype == GE_VTYPE_WEIGHT_8BIT >> GE_VTYPE_WEIGHT_SHIFT) { + fmtBase = DEC_U8_1; + } else if (weighttype == GE_VTYPE_WEIGHT_16BIT >> GE_VTYPE_WEIGHT_SHIFT) { + fmtBase = DEC_U16_1; + } else if (weighttype == GE_VTYPE_WEIGHT_FLOAT >> GE_VTYPE_WEIGHT_SHIFT) { + fmtBase = DEC_FLOAT_1; + } + } + + int numWeights = TranslateNumBones(nweights); + + if (numWeights <= 4) { + decFmt.w0off = decOff; + decFmt.w0fmt = fmtBase + numWeights - 1; + decOff += DecFmtSize(decFmt.w0fmt); + } else { + decFmt.w0off = decOff; + decFmt.w0fmt = fmtBase + 3; + decOff += DecFmtSize(decFmt.w0fmt); + decFmt.w1off = decOff; + decFmt.w1fmt = fmtBase + numWeights - 5; + decOff += DecFmtSize(decFmt.w1fmt); + } + } } if (tc) { @@ -1153,7 +1185,7 @@ void VertexDecoder::SetVertexType(u32 fmt, const VertexDecoderOptions &options, if (nrmalign[nrm] > biggest) biggest = nrmalign[nrm]; - if (skinning) { + if (skinInDecode) { steps_[numSteps_++] = morphcount == 1 ? nrmstep_skin[nrm] : nrmstep_morphskin[nrm]; // After skinning, we always have three floats. decFmt.nrmfmt = DEC_FLOAT_3; @@ -1204,7 +1236,7 @@ void VertexDecoder::SetVertexType(u32 fmt, const VertexDecoderOptions &options, steps_[numSteps_++] = posstep_through[pos]; decFmt.posfmt = DEC_FLOAT_3; } else { - if (skinning) { + if (skinInDecode) { steps_[numSteps_++] = morphcount == 1 ? posstep_skin[pos] : posstep_morph_skin[pos]; decFmt.posfmt = DEC_FLOAT_3; } else { From dbe52c8135bf03a20b8dc7fcb3abfaf0b6e24135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Apr 2018 13:09:01 +0200 Subject: [PATCH 19/40] Remove some obsolete checks - software skinning is now supported even if morphcount isn't 1. --- GPU/Common/VertexDecoderArm.cpp | 2 +- GPU/Common/VertexDecoderArm64.cpp | 2 +- GPU/Common/VertexDecoderCommon.cpp | 2 +- GPU/Common/VertexDecoderX86.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/GPU/Common/VertexDecoderArm.cpp b/GPU/Common/VertexDecoderArm.cpp index 1f7d65a833..5467515e9f 100644 --- a/GPU/Common/VertexDecoderArm.cpp +++ b/GPU/Common/VertexDecoderArm.cpp @@ -232,7 +232,7 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int // Add code to convert matrices to 4x4. // Later we might want to do this when the matrices are loaded instead. int boneCount = 0; - if (NEONSkinning && dec.weighttype && g_Config.bSoftwareSkinning && dec.morphcount == 1) { + if (NEONSkinning && dec.weighttype && g_Config.bSoftwareSkinning) { // Copying from R3 to R4 MOVP2R(R3, gstate.boneMatrix); MOVP2R(R4, bones); diff --git a/GPU/Common/VertexDecoderArm64.cpp b/GPU/Common/VertexDecoderArm64.cpp index aab6b4ca36..702dd6ae8d 100644 --- a/GPU/Common/VertexDecoderArm64.cpp +++ b/GPU/Common/VertexDecoderArm64.cpp @@ -196,7 +196,7 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int // Add code to convert matrices to 4x4. // Later we might want to do this when the matrices are loaded instead. int boneCount = 0; - if (dec.weighttype && g_Config.bSoftwareSkinning && dec.morphcount == 1) { + if (dec.weighttype && g_Config.bSoftwareSkinning) { // Copying from R3 to R4 MOVP2R(X3, gstate.boneMatrix); MOVP2R(X4, bones); diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index 97d8ced338..7d40fe5293 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -1082,7 +1082,7 @@ void VertexDecoder::SetVertexType(u32 fmt, const VertexDecoderOptions &options, DEBUG_LOG(G3D, "VTYPE: THRU=%i TC=%i COL=%i POS=%i NRM=%i WT=%i NW=%i IDX=%i MC=%i", (int)throughmode, tc, col, pos, nrm, weighttype, nweights, idx, morphcount); } - bool skinInDecode = weighttype != 0 && g_Config.bSoftwareSkinning && morphcount == 1; + bool skinInDecode = weighttype != 0 && g_Config.bSoftwareSkinning; if (weighttype) { // && nweights? weightoff = size; diff --git a/GPU/Common/VertexDecoderX86.cpp b/GPU/Common/VertexDecoderX86.cpp index 6961291a3f..1e63145628 100644 --- a/GPU/Common/VertexDecoderX86.cpp +++ b/GPU/Common/VertexDecoderX86.cpp @@ -208,7 +208,7 @@ JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int // Add code to convert matrices to 4x4. // Later we might want to do this when the matrices are loaded instead. int boneCount = 0; - if (dec.weighttype && g_Config.bSoftwareSkinning && dec.morphcount == 1) { + if (dec.weighttype && g_Config.bSoftwareSkinning) { MOV(PTRBITS, R(tempReg1), ImmPtr(&threeMasks)); MOVAPS(XMM4, MatR(tempReg1)); MOV(PTRBITS, R(tempReg1), ImmPtr(&aOne)); From 02ea4b5efa3289608c783551ad87569fcbb3af82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Apr 2018 14:37:32 +0200 Subject: [PATCH 20/40] Inner interpreter safety check - in hw skinning mode, we need to flush if any part of the vertex format changes. --- GPU/GPUCommon.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 0fb4318eb0..66e2dce1fe 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1553,6 +1553,10 @@ void GPUCommon::Execute_Prim(u32 op, u32 diff) { goto bail; #endif + uint32_t vtypeCheckMask = ~GE_VTYPE_WEIGHTCOUNT_MASK; + if (!g_Config.bSoftwareSkinning) + vtypeCheckMask = 0xFFFFFFFF; + while (src != stall) { uint32_t data = *src; switch (data >> 24) { @@ -1581,7 +1585,7 @@ void GPUCommon::Execute_Prim(u32 op, u32 diff) { { uint32_t diff = data ^ vertexType; // don't mask upper bits, vertexType is unmasked - if (diff & ~GE_VTYPE_WEIGHTCOUNT_MASK) { + if (diff & vtypeCheckMask) { goto bail; } else { vertexType = data; From 23052b5a6bd0c4ee8bbcd6950fb14e1aa91a8e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Apr 2018 16:28:06 +0200 Subject: [PATCH 21/40] Buildfix. --- GPU/GPUCommon.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 66e2dce1fe..713e4c4120 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1548,15 +1548,16 @@ void GPUCommon::Execute_Prim(u32 op, u32 diff) { // PRIM commands with other commands. A special case that might be interesting is that game // that changes culling mode between each prim, we could just change the triangle winding // right here to still be able to join draw calls. -#ifndef MOBILE_DEVICE - if (debugRecording_ || host->GPUDebuggingActive()) - goto bail; -#endif uint32_t vtypeCheckMask = ~GE_VTYPE_WEIGHTCOUNT_MASK; if (!g_Config.bSoftwareSkinning) vtypeCheckMask = 0xFFFFFFFF; +#ifndef MOBILE_DEVICE + if (debugRecording_ || host->GPUDebuggingActive()) + goto bail; +#endif + while (src != stall) { uint32_t data = *src; switch (data >> 24) { From f551d2742b206f093100626d1d295d2280c900c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 10 Apr 2018 16:29:26 +0200 Subject: [PATCH 22/40] Update lang submodule (restore software skinning option) --- assets/lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/lang b/assets/lang index 36d367e8d6..44a654736d 160000 --- a/assets/lang +++ b/assets/lang @@ -1 +1 @@ -Subproject commit 36d367e8d62ade82211e7c5eeacd3a6c122702d0 +Subproject commit 44a654736d67b36a55171b1862d41df2dc19cb25 From 3322adbc2265d81c0201d52e1813a2903b23d916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 11 Apr 2018 11:16:41 +0200 Subject: [PATCH 23/40] IR Interpreter: Add some missing instruction metadata. May help part of #10897 --- Core/MIPS/IR/IRInst.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Core/MIPS/IR/IRInst.cpp b/Core/MIPS/IR/IRInst.cpp index 7ad98f849f..300487e0ba 100644 --- a/Core/MIPS/IR/IRInst.cpp +++ b/Core/MIPS/IR/IRInst.cpp @@ -156,12 +156,16 @@ static const IRMeta irMeta[] = { { IROp::ExitToConstIfLtZ, "ExitIfLtZ", "CG", IRFLAG_EXIT }, { IROp::ExitToReg, "ExitToReg", "_G", IRFLAG_EXIT }, { IROp::Syscall, "Syscall", "_C", IRFLAG_EXIT }, - { IROp::Break, "Break", "", IRFLAG_EXIT}, + { IROp::Break, "Break", "", IRFLAG_EXIT }, { IROp::SetPC, "SetPC", "_G" }, { IROp::SetPCConst, "SetPC", "_C" }, { IROp::CallReplacement, "CallRepl", "_C" }, { IROp::Breakpoint, "Breakpoint", "", IRFLAG_EXIT }, { IROp::MemoryCheck, "MemoryCheck", "_GC", IRFLAG_EXIT }, + + { IROp::RestoreRoundingMode, "RestoreRoundingMode", "" }, + { IROp::ApplyRoundingMode, "ApplyRoundingMode", "" }, + { IROp::UpdateRoundingMode, "UpdateRoundingMode", "" }, }; const IRMeta *metaIndex[256]; From b4721fbc44d5cfaf80e0e8819f7706efc98efa60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 11 Apr 2018 11:55:12 +0200 Subject: [PATCH 24/40] Temporary workaround for another IR interpreter crash. See #10897 --- Core/MIPS/IR/IRInterpreter.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index dee961f98e..045b69f469 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -977,7 +977,18 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count) { } break; + case IROp::ApplyRoundingMode: + // TODO: Implement + break; + case IROp::RestoreRoundingMode: + // TODO: Implement + break; + case IROp::UpdateRoundingMode: + // TODO: Implement + break; + default: + // Unimplemented IR op. Bad. Crash(); } #ifdef _DEBUG From 9e75250c4e28f3968ec05becf6db31da00dd2dda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 11 Apr 2018 13:45:18 +0200 Subject: [PATCH 25/40] Android: Do the same file_offset_bits checks for pread64 as before. Should help #10901 --- Core/FileLoaders/LocalFileLoader.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Core/FileLoaders/LocalFileLoader.cpp b/Core/FileLoaders/LocalFileLoader.cpp index 389f47d652..a537ee0211 100644 --- a/Core/FileLoaders/LocalFileLoader.cpp +++ b/Core/FileLoaders/LocalFileLoader.cpp @@ -115,7 +115,11 @@ size_t LocalFileLoader::ReadAt(s64 absolutePos, size_t bytes, size_t count, void #if PPSSPP_PLATFORM(ANDROID) // pread64 doesn't appear to actually be 64-bit safe, though such ISOs are uncommon. See #10862. if (absolutePos <= 0x7FFFFFFF) { +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS < 64 return pread64(fd_, data, bytes * count, absolutePos) / bytes; +#else + return pread(fd_, data, bytes * count, absolutePos) / bytes; +#endif } else { // Since pread64 doesn't change the file offset, it should be safe to avoid the lock in the common case. std::lock_guard guard(readLock_); From 26f5b5b9389d9c010ee3a933e865268fa18897c2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 11 Apr 2018 21:31:36 -0700 Subject: [PATCH 26/40] Replay: Fix a silly typo applying analog. --- Core/Replay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/Replay.cpp b/Core/Replay.cpp index 9da036574e..e35458fb26 100644 --- a/Core/Replay.cpp +++ b/Core/Replay.cpp @@ -292,7 +292,7 @@ static void ReplayExecuteCtrl(uint32_t &buttons, uint8_t analog[2][2], uint64_t // We have to always apply the latest state here, because otherwise real input is used between changes. buttons = lastButtons; - memcpy(analog, lastAnalog, sizeof(analog)); + memcpy(analog, lastAnalog, sizeof(lastAnalog)); if (replayExecPos < replayCtrlPos) { replayExecPos = replayCtrlPos; From 31ae11ecc86021edf782bb54645ce798c49dc241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 12 Apr 2018 11:59:18 +0200 Subject: [PATCH 27/40] Don't forget to dirty some state after depal. May help #10906 ? --- GPU/Vulkan/TextureCacheVulkan.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 1b87eef0f3..6f1c14ad76 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -425,11 +425,12 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr // Need to rebind the pipeline since we switched it. drawEngine_->DirtyPipeline(); - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE); + // Since we may have switched render targets, we need to re-set depth/stencil etc states. + gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE); } else { entry->status &= ~TexCacheEntry::STATUS_DEPALETTIZE; - framebufferManager_->RebindFramebuffer(); + framebufferManager_->RebindFramebuffer(); // TODO: This line should not be needed? imageView_ = framebufferManagerVulkan_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET); gstate_c.SetTextureFullAlpha(gstate.getTextureFormat() == GE_TFMT_5650); From a3ed87bca528b4070d3fdc146f8fee0a79be45cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 12 Apr 2018 12:00:19 +0200 Subject: [PATCH 28/40] Some comment fixes and cleanup. Not much point warning for those MIPS instructions - if games use them, they use them carefully because games can't catch that exception anyway. --- Core/MIPS/MIPSInt.cpp | 16 +++------------- GPU/Vulkan/DrawEngineVulkan.cpp | 9 +++------ 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/Core/MIPS/MIPSInt.cpp b/Core/MIPS/MIPSInt.cpp index a1273526e6..0c29cccdae 100644 --- a/Core/MIPS/MIPSInt.cpp +++ b/Core/MIPS/MIPSInt.cpp @@ -375,19 +375,9 @@ namespace MIPSInt { case 10: if (R(rt) == 0) R(rd) = R(rs); break; //movz case 11: if (R(rt) != 0) R(rd) = R(rs); break; //movn - case 32: - if (!has_warned) { - ERROR_LOG(CPU,"WARNING : exception-causing add at %08x", PC); - has_warned = true; - } - R(rd) = R(rs) + R(rt); break; //add + case 32: R(rd) = R(rs) + R(rt); break; //add (exception on overflow) case 33: R(rd) = R(rs) + R(rt); break; //addu - case 34: - if (!has_warned) { - ERROR_LOG(CPU,"WARNING : exception-causing sub at %08x", PC); - has_warned = true; - } - R(rd) = R(rs) - R(rt); break; //sub + case 34: R(rd) = R(rs) - R(rt); break; //sub (exception on overflow) case 35: R(rd) = R(rs) - R(rt); break; //subu case 36: R(rd) = R(rs) & R(rt); break; //and case 37: R(rd) = R(rs) | R(rt); break; //or @@ -398,7 +388,7 @@ namespace MIPSInt case 44: R(rd) = ((s32)R(rs) > (s32)R(rt)) ? R(rs) : R(rt); break; //max case 45: R(rd) = ((s32)R(rs) < (s32)R(rt)) ? R(rs) : R(rt); break;//min default: - _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); + _dbg_assert_msg_(CPU, 0, "Unknown MIPS instruction %08x", op.encoding); break; } PC += 4; diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 5b5275d77e..e8310abd18 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -1003,14 +1003,11 @@ DrawEngineVulkan::TessellationDataTransferVulkan::TessellationDataTransferVulkan DrawEngineVulkan::TessellationDataTransferVulkan::~TessellationDataTransferVulkan() { } -// TODO: Consolidate the three textures into one, with height 3. -// This can be done for all the backends. -// TODO: Actually, even better, avoid the usage of textures altogether and just use shader storage buffers from the current pushbuffer. void DrawEngineVulkan::TessellationDataTransferVulkan::PrepareBuffers(float *&pos, float *&tex, float *&col, int &posStride, int &texStride, int &colStride, int size, bool hasColor, bool hasTexCoords) { colStride = 4; - // TODO: This SHOULD work without padding but I can't get it to work on nvidia, so had - // to expand to vec4. Driver bug? + // SSBOs that are not simply float1 or float2 need to be padded up to a float4 size. vec3 members + // also need to be 16-byte aligned, hence the padding. struct TessData { float pos[3]; float pad1; float uv[2]; float pad2[2]; @@ -1030,5 +1027,5 @@ void DrawEngineVulkan::TessellationDataTransferVulkan::PrepareBuffers(float *&po } void DrawEngineVulkan::TessellationDataTransferVulkan::SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) { - // Nothing to do here! + // Nothing to do here! The caller will write directly to the pushbuffer through the pointers it got through PrepareBuffers. } From dd228be6982986ac6aa2f1c9fdb805d5a0f5e3be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 12 Apr 2018 14:35:04 +0200 Subject: [PATCH 29/40] GL: Add a lot of conditional error checks (normally not enabled) --- ext/native/thin3d/GLQueueRunner.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index c312f468e7..699821a285 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -68,6 +68,7 @@ void GLQueueRunner::DestroyDeviceObjects() { } void GLQueueRunner::RunInitSteps(const std::vector &steps) { + CHECK_GL_ERROR_IF_DEBUG(); glActiveTexture(GL_TEXTURE0); GLuint boundTexture = (GLuint)-1; bool allocatedTextures = false; @@ -102,6 +103,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { } case GLRInitStepType::CREATE_PROGRAM: { + CHECK_GL_ERROR_IF_DEBUG(); GLRProgram *program = step.create_program.program; program->program = glCreateProgram(); _assert_msg_(G3D, step.create_program.num_shaders > 0, "Can't create a program with zero shaders"); @@ -162,6 +164,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { } else { ELOG("Could not link program with %d shaders for unknown reason:", step.create_program.num_shaders); } + CHECK_GL_ERROR_IF_DEBUG(); break; } @@ -185,10 +188,12 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { } } } + CHECK_GL_ERROR_IF_DEBUG(); break; } case GLRInitStepType::CREATE_SHADER: { + CHECK_GL_ERROR_IF_DEBUG(); GLuint shader = glCreateShader(step.create_shader.stage); step.create_shader.shader->shader = shader; const char *code = step.create_shader.code; @@ -221,6 +226,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { step.create_shader.shader->code = code; delete[] step.create_shader.code; step.create_shader.shader->valid = true; + CHECK_GL_ERROR_IF_DEBUG(); break; } case GLRInitStepType::CREATE_INPUT_LAYOUT: @@ -231,9 +237,11 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { } case GLRInitStepType::CREATE_FRAMEBUFFER: { + CHECK_GL_ERROR_IF_DEBUG(); boundTexture = (GLuint)-1; InitCreateFramebuffer(step); allocatedTextures = true; + CHECK_GL_ERROR_IF_DEBUG(); break; } case GLRInitStepType::TEXTURE_IMAGE: @@ -263,10 +271,12 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { glTexParameteri(tex->target, GL_TEXTURE_WRAP_T, tex->wrapT); glTexParameteri(tex->target, GL_TEXTURE_MAG_FILTER, tex->magFilter); glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, tex->minFilter); + CHECK_GL_ERROR_IF_DEBUG(); break; } case GLRInitStepType::TEXTURE_FINALIZE: { + CHECK_GL_ERROR_IF_DEBUG(); GLRTexture *tex = step.texture_finalize.texture; if (boundTexture != tex->texture) { glBindTexture(tex->target, tex->texture); @@ -277,13 +287,16 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { if (step.texture_finalize.genMips) { glGenerateMipmap(tex->target); } + CHECK_GL_ERROR_IF_DEBUG(); break; } default: + CHECK_GL_ERROR_IF_DEBUG(); Crash(); break; } } + CHECK_GL_ERROR_IF_DEBUG(); // TODO: Use GL_KHR_no_error or a debug callback, where supported? if (allocatedTextures) { @@ -752,6 +765,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glBindTexture(GL_TEXTURE_2D, 0); // Which target? Well we only use this one anyway... curTex[slot] = nullptr; } + CHECK_GL_ERROR_IF_DEBUG(); break; } case GLRRenderCommand::BIND_FB_TEXTURE: @@ -769,6 +783,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { // TODO: Depth texturing? curTex[slot] = nullptr; } + CHECK_GL_ERROR_IF_DEBUG(); break; } case GLRRenderCommand::BINDPROGRAM: @@ -804,6 +819,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { auto &entry = layout->entries[i]; glVertexAttribPointer(entry.location, entry.count, entry.type, entry.normalized, entry.stride, (const void *)(c.bindVertexBuffer.offset + entry.offset)); } + CHECK_GL_ERROR_IF_DEBUG(); break; } case GLRRenderCommand::BIND_BUFFER: @@ -822,6 +838,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { assert(!c.bind_buffer.buffer->Mapped()); glBindBuffer(c.bind_buffer.target, buf); } + CHECK_GL_ERROR_IF_DEBUG(); break; } case GLRRenderCommand::GENMIPS: @@ -841,6 +858,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { break; case GLRRenderCommand::TEXTURESAMPLER: { + CHECK_GL_ERROR_IF_DEBUG(); GLint slot = c.textureSampler.slot; if (slot != activeSlot) { glActiveTexture(GL_TEXTURE0 + slot); @@ -874,6 +892,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { } tex->anisotropy = c.textureSampler.anisotropy; } + CHECK_GL_ERROR_IF_DEBUG(); break; } case GLRRenderCommand::TEXTURELOD: @@ -957,6 +976,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glDisable(GL_COLOR_LOGIC_OP); #endif glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + CHECK_GL_ERROR_IF_DEBUG(); } void GLQueueRunner::PerformCopy(const GLRStep &step) { @@ -1016,6 +1036,7 @@ void GLQueueRunner::PerformCopy(const GLRStep &step) { srcRect.w, srcRect.h, depth); } #endif + CHECK_GL_ERROR_IF_DEBUG(); } void GLQueueRunner::PerformReadback(const GLRStep &pass) { From f4d8bfdf187f909e7e64ca63d49ea7c9ee13d146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 12 Apr 2018 14:35:25 +0200 Subject: [PATCH 30/40] Avoid some GL errors on older devices --- ext/native/thin3d/GLQueueRunner.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 699821a285..ce13132cce 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -282,7 +282,9 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { glBindTexture(tex->target, tex->texture); boundTexture = tex->texture; } - glTexParameteri(tex->target, GL_TEXTURE_MAX_LEVEL, step.texture_finalize.maxLevel); + if (!gl_extensions.IsGLES || gl_extensions.GLES3) { + glTexParameteri(tex->target, GL_TEXTURE_MAX_LEVEL, step.texture_finalize.maxLevel); + } tex->maxLod = (float)step.texture_finalize.maxLevel; if (step.texture_finalize.genMips) { glGenerateMipmap(tex->target); @@ -346,7 +348,9 @@ void GLQueueRunner::InitCreateFramebuffer(const GLRInitStep &step) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, fbo->color_texture.wrapT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, fbo->color_texture.magFilter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, fbo->color_texture.minFilter); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + if (!gl_extensions.IsGLES || gl_extensions.GLES3) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + } if (gl_extensions.IsGLES) { if (gl_extensions.OES_packed_depth_stencil) { From 163350bbcd906535c31b33d276c179eb920b39dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 13 Apr 2018 08:56:17 +0200 Subject: [PATCH 31/40] Vulkan/D3D11: Make some space in the base uniform buffer by consolidating the spline parameters into one variable. --- GPU/Common/ShaderUniforms.cpp | 5 +---- GPU/Common/ShaderUniforms.h | 18 +++++++++--------- GPU/Directx9/VertexShaderGeneratorDX9.cpp | 4 ++++ GPU/Vulkan/ShaderManagerVulkan.cpp | 6 +++--- GPU/Vulkan/VertexShaderGeneratorVulkan.cpp | 16 ++++++++++------ ext/native/math/dataconv.h | 4 ++++ 6 files changed, 31 insertions(+), 22 deletions(-) diff --git a/GPU/Common/ShaderUniforms.cpp b/GPU/Common/ShaderUniforms.cpp index bc02f60912..6a5a7572fe 100644 --- a/GPU/Common/ShaderUniforms.cpp +++ b/GPU/Common/ShaderUniforms.cpp @@ -198,10 +198,7 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView } if (dirtyUniforms & DIRTY_BEZIERSPLINE) { - ub->spline_count_u = gstate_c.spline_count_u; - ub->spline_count_v = gstate_c.spline_count_v; - ub->spline_type_u = gstate_c.spline_type_u; - ub->spline_type_v = gstate_c.spline_type_v; + ub->spline_counts = BytesToUint32(gstate_c.spline_count_u, gstate_c.spline_count_v, gstate_c.spline_type_u, gstate_c.spline_type_v); } } diff --git a/GPU/Common/ShaderUniforms.h b/GPU/Common/ShaderUniforms.h index 55cdaf6efd..a4c6d8f936 100644 --- a/GPU/Common/ShaderUniforms.h +++ b/GPU/Common/ShaderUniforms.h @@ -30,7 +30,7 @@ struct UB_VS_FS_Base { float depthRange[4]; float fogCoef[2]; float stencil; float pad0; float matAmbient[4]; - int spline_count_u; int spline_count_v; int spline_type_u; int spline_type_v; + uint32_t spline_counts; int pad1; int pad2; int pad3; // Fragment data float fogColor[4]; float texEnvColor[4]; @@ -53,10 +53,10 @@ R"( mat4 proj_mtx; vec2 fogcoef; float stencilReplace; vec4 matambientalpha; - int spline_count_u; - int spline_count_v; - int spline_type_u; - int spline_type_v; + uint spline_counts; + int pad1; + int pad2; + int pad3; vec3 fogcolor; vec3 texenv; ivec4 alphacolorref; @@ -79,10 +79,10 @@ R"( float4x4 u_proj; float2 u_fogcoef; float u_stencilReplaceValue; float4 u_matambientalpha; - int u_spline_count_u; - int u_spline_count_v; - int u_spline_type_u; - int u_spline_type_v; + uint u_spline_counts; + int pad1; + int pad2; + int pad3; float3 u_fogcolor; float3 u_texenv; uint4 u_alphacolorref; diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.cpp b/GPU/Directx9/VertexShaderGeneratorDX9.cpp index ed397500b4..7b1b542558 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.cpp +++ b/GPU/Directx9/VertexShaderGeneratorDX9.cpp @@ -390,6 +390,8 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage if (!enableBones) { // Hardware tessellation if (doSpline || doBezier) { + WRITE(p, " uint u_spline_count_u = u_spline_counts & 0xFF;\n"); + WRITE(p, " uint u_spline_count_v = (u_spline_counts >> 8) & 0xFF;\n"); WRITE(p, " uint num_patches_u = %s;\n", doBezier ? "(u_spline_count_u - 1) / 3u" : "u_spline_count_u - 3"); WRITE(p, " float2 tess_pos = In.position.xy;\n"); WRITE(p, " int u = In.instanceId %% num_patches_u;\n"); @@ -420,6 +422,8 @@ void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage WRITE(p, " weights[3] = tess_pos * tess_pos * tess_pos;\n"); } else if (doSpline) { WRITE(p, " int2 spline_num_patches = int2(u_spline_count_u - 3, u_spline_count_v - 3);\n"); + WRITE(p, " int u_spline_type_u = (u_spline_counts >> 16) & 0xFF;\n"); + WRITE(p, " int u_spline_type_v = (u_spline_counts >> 24) & 0xFF;\n"); WRITE(p, " int2 spline_type = int2(u_spline_type_u, u_spline_type_v);\n"); WRITE(p, " float2 knots[6];\n"); WRITE(p, " spline_knot(spline_num_patches, spline_type, knots, patch_pos);\n"); diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 17106e0951..8834aa3717 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -20,6 +20,7 @@ #endif #include "base/logging.h" +#include "base/stringutil.h" #include "math/lin/matrix4x4.h" #include "math/math_util.h" #include "math/dataconv.h" @@ -48,7 +49,7 @@ VulkanFragmentShader::VulkanFragmentShader(VulkanContext *vulkan, FShaderID id, std::string errorMessage; std::vector spirv; #ifdef SHADERLOG - OutputDebugStringA(code); + OutputDebugStringA(LineNumberString(code).c_str()); #endif bool success = GLSLtoSPV(VK_SHADER_STAGE_FRAGMENT_BIT, code, spirv, &errorMessage); @@ -63,7 +64,6 @@ VulkanFragmentShader::VulkanFragmentShader(VulkanContext *vulkan, FShaderID id, #ifdef SHADERLOG OutputDebugStringA("Messages:\n"); OutputDebugStringA(errorMessage.c_str()); - OutputDebugStringA(code); #endif Reporting::ReportMessage("Vulkan error in shader compilation: info: %s / code: %s", errorMessage.c_str(), code); } else { @@ -105,7 +105,7 @@ VulkanVertexShader::VulkanVertexShader(VulkanContext *vulkan, VShaderID id, cons std::string errorMessage; std::vector spirv; #ifdef SHADERLOG - OutputDebugStringA(code); + OutputDebugStringA(LineNumberString(code).c_str()); #endif bool success = GLSLtoSPV(VK_SHADER_STAGE_VERTEX_BIT, code, spirv, &errorMessage); if (!errorMessage.empty()) { diff --git a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp index d7517cba6a..d72d8c904a 100644 --- a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp @@ -216,14 +216,14 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) { WRITE(p, "out gl_PerVertex { vec4 gl_Position; };\n"); if (doBezier || doSpline) { - WRITE(p, "layout (std430) struct TessData {\n"); + WRITE(p, "struct TessData {\n"); WRITE(p, " vec4 pos;\n"); WRITE(p, " vec4 uv;\n"); WRITE(p, " vec4 color;\n"); WRITE(p, "};"); WRITE(p, "layout (std430, set = 0, binding = 5) buffer s_tess_data {\n"); WRITE(p, " TessData data[];"); - WRITE(p, "} tess_data;"); + WRITE(p, "} tess_data;\n"); for (int i = 2; i <= 4; i++) { // Define 3 types vec2, vec3, vec4 @@ -334,13 +334,15 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) { WRITE(p, " vec3 _pos[16];\n"); WRITE(p, " vec2 _tex[16];\n"); WRITE(p, " vec4 _col[16];\n"); - WRITE(p, " int num_patches_u = %s;\n", doBezier ? "(base.spline_count_u - 1) / 3" : "base.spline_count_u - 3"); + WRITE(p, " int spline_count_u = int(base.spline_counts & 0xff);\n"); + WRITE(p, " int spline_count_v = int((base.spline_counts >> 8) & 0xff);\n"); + WRITE(p, " int num_patches_u = %s;\n", doBezier ? "(spline_count_u - 1) / 3" : "spline_count_u - 3"); WRITE(p, " int u = int(mod(gl_InstanceIndex, num_patches_u));\n"); WRITE(p, " int v = gl_InstanceIndex / num_patches_u;\n"); WRITE(p, " ivec2 patch_pos = ivec2(u, v);\n"); WRITE(p, " for (int i = 0; i < 4; i++) {\n"); WRITE(p, " for (int j = 0; j < 4; j++) {\n"); - WRITE(p, " int idx = (i + v%s) * base.spline_count_u + (j + u%s);\n", doBezier ? " * 3" : "", doBezier ? " * 3" : ""); + WRITE(p, " int idx = (i + v%s) * spline_count_u + (j + u%s);\n", doBezier ? " * 3" : "", doBezier ? " * 3" : ""); WRITE(p, " _pos[i * 4 + j] = tess_data.data[idx].pos.xyz;\n"); if (doTexture && hasTexcoord && hasTexcoordTess) WRITE(p, " _tex[i * 4 + j] = tess_data.data[idx].uv.xy;\n"); @@ -357,8 +359,10 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) { WRITE(p, " weights[2] = 3 * tess_pos * tess_pos * (1 - tess_pos);\n"); WRITE(p, " weights[3] = tess_pos * tess_pos * tess_pos;\n"); } else { // Spline - WRITE(p, " ivec2 spline_num_patches = ivec2(base.spline_count_u - 3, base.spline_count_v - 3);\n"); - WRITE(p, " ivec2 spline_type = ivec2(base.spline_type_u, base.spline_type_v);\n"); + WRITE(p, " ivec2 spline_num_patches = ivec2(spline_count_u - 3, spline_count_v - 3);\n"); + WRITE(p, " int spline_type_u = int((base.spline_counts >> 16) & 0xff);\n"); + WRITE(p, " int spline_type_v = int((base.spline_counts >> 24) & 0xff);\n"); + WRITE(p, " ivec2 spline_type = ivec2(spline_type_u, spline_type_v);\n"); WRITE(p, " vec2 knots[6];\n"); WRITE(p, " spline_knot(spline_num_patches, spline_type, knots, patch_pos);\n"); WRITE(p, " spline_weight(tess_pos + patch_pos, knots, weights);\n"); diff --git a/ext/native/math/dataconv.h b/ext/native/math/dataconv.h index 84e9da1a7e..30619fc3f9 100644 --- a/ext/native/math/dataconv.h +++ b/ext/native/math/dataconv.h @@ -157,3 +157,7 @@ inline void ExpandFloat24x3ToFloat4(float dest[4], const uint32_t src[3]) { memcpy(dest, temp, sizeof(float) * 4); #endif } + +inline uint32_t BytesToUint32(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { + return (a) | (b << 8) | (c << 16) | (d << 24); +} \ No newline at end of file From 117dad8a485335fff235250bde6efdd950959b71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 13 Apr 2018 09:11:08 +0200 Subject: [PATCH 32/40] Just add some constants for the GL texture slots. --- GPU/GLES/DrawEngineGLES.cpp | 6 +++--- GPU/GLES/DrawEngineGLES.h | 11 +++++++++++ GPU/GLES/FramebufferManagerGLES.cpp | 2 +- GPU/GLES/StateMappingGLES.cpp | 4 ++-- GPU/GLES/TextureCacheGLES.cpp | 8 ++++---- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 25822b470d..2241a18dd3 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -657,7 +657,7 @@ void DrawEngineGLES::TessellationDataTransferGLES::SendDataToShader(const float data_tex[0] = renderManager_->CreateTexture(GL_TEXTURE_2D); renderManager_->TextureImage(data_tex[0], 0, size, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT, pos_data, GLRAllocType::NEW, false); renderManager_->FinalizeTexture(data_tex[0], 0, false); - renderManager_->BindTexture(4, data_tex[0]); + renderManager_->BindTexture(TEX_SLOT_SPLINE_POS, data_tex[0]); // Texcoords if (hasTexCoords) { @@ -668,7 +668,7 @@ void DrawEngineGLES::TessellationDataTransferGLES::SendDataToShader(const float data_tex[1] = renderManager_->CreateTexture(GL_TEXTURE_2D); renderManager_->TextureImage(data_tex[1], 0, size, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT, tex_data, GLRAllocType::NEW, false); renderManager_->FinalizeTexture(data_tex[1], 0, false); - renderManager_->BindTexture(5, data_tex[1]); + renderManager_->BindTexture(TEX_SLOT_SPLINE_NRM, data_tex[1]); } if (data_tex[2]) @@ -680,7 +680,7 @@ void DrawEngineGLES::TessellationDataTransferGLES::SendDataToShader(const float renderManager_->TextureImage(data_tex[2], 0, sizeColor, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT, col_data, GLRAllocType::NEW, false); renderManager_->FinalizeTexture(data_tex[2], 0, false); - renderManager_->BindTexture(6, data_tex[2]); + renderManager_->BindTexture(TEX_SLOT_SPLINE_COL, data_tex[2]); } void DrawEngineGLES::TessellationDataTransferGLES::EndFrame() { diff --git a/GPU/GLES/DrawEngineGLES.h b/GPU/GLES/DrawEngineGLES.h index 42e4ce5133..535535d42e 100644 --- a/GPU/GLES/DrawEngineGLES.h +++ b/GPU/GLES/DrawEngineGLES.h @@ -41,6 +41,17 @@ struct TransformedVertex; struct DecVtxFormat; +enum { + TEX_SLOT_PSP_TEXTURE = 0, + TEX_SLOT_SHADERBLEND_SRC = 1, + TEX_SLOT_ALPHATEST = 2, + TEX_SLOT_CLUT = 3, + TEX_SLOT_SPLINE_POS = 4, + TEX_SLOT_SPLINE_NRM = 5, + TEX_SLOT_SPLINE_COL = 6, +}; + + // States transitions: // On creation: DRAWN_NEW // DRAWN_NEW -> DRAWN_HASHING diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 7246c5654c..1211459f02 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -403,7 +403,7 @@ void FramebufferManagerGLES::MakePixelTexture(const u8 *srcPixels, GEBufferForma render_->FinalizeTexture(drawPixelsTex_, 0, false); // TODO: Return instead? - render_->BindTexture(0, drawPixelsTex_); + render_->BindTexture(TEX_SLOT_PSP_TEXTURE, drawPixelsTex_); } void FramebufferManagerGLES::SetViewport2D(int x, int y, int w, int h) { diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index 5a5fc1f071..ec256cd842 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -124,7 +124,7 @@ static const GLushort logicOps[] = { inline void DrawEngineGLES::ResetShaderBlending() { if (fboTexBound_) { GLRenderManager *renderManager = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); - renderManager->BindTexture(1, nullptr); + renderManager->BindTexture(TEX_SLOT_SHADERBLEND_SRC, nullptr); fboTexBound_ = false; } } @@ -331,7 +331,7 @@ void DrawEngineGLES::ApplyDrawStateLate(bool setStencil, int stencilValue) { if (!gstate.isModeClear()) { // Apply last, once we know the alpha params of the texture. if (gstate.isAlphaTestEnabled() || gstate.isColorTestEnabled()) { - fragmentTestCache_->BindTestTexture(2); + fragmentTestCache_->BindTestTexture(TEX_SLOT_ALPHATEST); } } } diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index ae57b3b472..38d5aa280a 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -327,7 +327,7 @@ void TextureCacheGLES::BindTexture(TexCacheEntry *entry) { } void TextureCacheGLES::Unbind() { - render_->BindTexture(0, nullptr); + render_->BindTexture(TEX_SLOT_PSP_TEXTURE, nullptr); InvalidateLastTexture(); } @@ -451,8 +451,8 @@ void TextureCacheGLES::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFram shaderApply.Use(render_, drawEngine_, shadeInputLayout_); framebufferManagerGL_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_SKIP_COPY); - render_->BindTexture(3, clutTexture); - render_->SetTextureSampler(3, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_NEAREST, GL_NEAREST, 0.0f); + render_->BindTexture(TEX_SLOT_CLUT, clutTexture); + render_->SetTextureSampler(TEX_SLOT_CLUT, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_NEAREST, GL_NEAREST, 0.0f); shaderApply.Shade(render_); @@ -662,7 +662,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) { // This will rebind it, but that's okay. // Need to actually bind it now - it might only have gotten bound in the init phase. - render_->BindTexture(0, entry->textureName); + render_->BindTexture(TEX_SLOT_PSP_TEXTURE, entry->textureName); UpdateSamplingParams(*entry, true); } From 29c41c6a359846835b57897704da1c63ca01c00e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 13 Apr 2018 12:25:57 +0200 Subject: [PATCH 33/40] Implement shader depal for Vulkan. See #10908. Bilinear filter not yet implemented. --- GPU/Common/DepalettizeShaderCommon.cpp | 2 +- GPU/Common/ShaderCommon.h | 7 ++- GPU/Common/ShaderId.cpp | 4 ++ GPU/Common/ShaderId.h | 2 +- GPU/Common/ShaderUniforms.cpp | 11 ++++ GPU/Common/ShaderUniforms.h | 9 +-- GPU/GPUState.h | 3 + GPU/Vulkan/DrawEngineVulkan.cpp | 63 +++++++++++++------- GPU/Vulkan/DrawEngineVulkan.h | 15 +++-- GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp | 41 ++++++++++++- GPU/Vulkan/ShaderManagerVulkan.cpp | 6 +- GPU/Vulkan/TextureCacheVulkan.cpp | 27 +++++++-- GPU/Vulkan/VertexShaderGeneratorVulkan.cpp | 8 +-- 13 files changed, 151 insertions(+), 47 deletions(-) diff --git a/GPU/Common/DepalettizeShaderCommon.cpp b/GPU/Common/DepalettizeShaderCommon.cpp index a230ce163c..0ff61cc995 100644 --- a/GPU/Common/DepalettizeShaderCommon.cpp +++ b/GPU/Common/DepalettizeShaderCommon.cpp @@ -70,7 +70,7 @@ void GenerateDepalShader300(char *buffer, GEBufferFormat pixelFormat, ShaderLang int mask = gstate.getClutIndexMask(); int shift = gstate.getClutIndexShift(); int offset = gstate.getClutIndexStartPos(); - const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); + GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); // Unfortunately sampling turned our texture into floating point. To avoid this, might be able // to declare them as isampler2D objects, but these require integer textures, which needs more work. // Anyhow, we simply work around this by converting back to integer. Hopefully there will be no loss of precision. diff --git a/GPU/Common/ShaderCommon.h b/GPU/Common/ShaderCommon.h index 6e4bfa8c81..c261858854 100644 --- a/GPU/Common/ShaderCommon.h +++ b/GPU/Common/ShaderCommon.h @@ -85,15 +85,16 @@ enum : uint64_t { DIRTY_BONEMATRIX6 = 1ULL << 30, DIRTY_BONEMATRIX7 = 1ULL << 31, - // These are for hardware tessellation DIRTY_BEZIERSPLINE = 1ULL << 32, DIRTY_TEXCLAMP = 1ULL << 33, - // space for 7 more uniforms. + DIRTY_DEPAL = 1ULL << 34, + + // space for 5 more uniform dirty flags. Remember to update DIRTY_ALL_UNIFORMS. DIRTY_BONE_UNIFORMS = 0xFF000000ULL, - DIRTY_ALL_UNIFORMS = 0x3FFFFFFFFULL, + DIRTY_ALL_UNIFORMS = 0x7FFFFFFFFULL, DIRTY_ALL_LIGHTS = DIRTY_LIGHT0 | DIRTY_LIGHT1 | DIRTY_LIGHT2 | DIRTY_LIGHT3, // Other dirty elements that aren't uniforms! diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index 3a8f0de37e..4a33f7c216 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -171,6 +171,7 @@ std::string FragmentShaderDesc(const ShaderID &id) { if (id.Bit(FS_BIT_COLOR_DOUBLE)) desc << "2x "; if (id.Bit(FS_BIT_FLATSHADE)) desc << "Flat "; if (id.Bit(FS_BIT_BGRA_TEXTURE)) desc << "BGRA "; + if (id.Bit(FS_BIT_SHADER_DEPAL)) desc << "Depal "; if (id.Bit(FS_BIT_SHADER_TEX_CLAMP)) { desc << "TClamp"; if (id.Bit(FS_BIT_CLAMP_S)) desc << "S"; @@ -236,6 +237,7 @@ void ComputeFragmentShaderID(ShaderID *id_out) { bool doTextureProjection = (gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX && MatrixNeedsProjection(gstate.tgenMatrix)); bool doTextureAlpha = gstate.isTextureAlphaUsed(); bool doFlatShading = gstate.getShadeMode() == GE_SHADE_FLAT; + bool useShaderDepal = gstate_c.useShaderDepal; ReplaceBlendType replaceBlend = ReplaceBlendWithShader(gstate_c.allowShaderBlend, gstate.FrameBufFormat()); ReplaceAlphaType stencilToAlpha = ReplaceAlphaWithStencil(replaceBlend); @@ -299,6 +301,8 @@ void ComputeFragmentShaderID(ShaderID *id_out) { id.SetBits(FS_BIT_BLENDFUNC_B, 4, gstate.getBlendFuncB()); } id.SetBit(FS_BIT_FLATSHADE, doFlatShading); + + id.SetBit(FS_BIT_SHADER_DEPAL, useShaderDepal); } *id_out = id; diff --git a/GPU/Common/ShaderId.h b/GPU/Common/ShaderId.h index afc7c51313..7d2e1b1cdd 100644 --- a/GPU/Common/ShaderId.h +++ b/GPU/Common/ShaderId.h @@ -62,7 +62,7 @@ enum { FS_BIT_DO_TEXTURE = 1, FS_BIT_TEXFUNC = 2, // 3 bits FS_BIT_TEXALPHA = 5, - // 6 is free. + FS_BIT_SHADER_DEPAL = 6, FS_BIT_SHADER_TEX_CLAMP = 7, FS_BIT_CLAMP_S = 8, FS_BIT_CLAMP_T = 9, diff --git a/GPU/Common/ShaderUniforms.cpp b/GPU/Common/ShaderUniforms.cpp index 6a5a7572fe..2e57ee6b72 100644 --- a/GPU/Common/ShaderUniforms.cpp +++ b/GPU/Common/ShaderUniforms.cpp @@ -200,6 +200,17 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView if (dirtyUniforms & DIRTY_BEZIERSPLINE) { ub->spline_counts = BytesToUint32(gstate_c.spline_count_u, gstate_c.spline_count_v, gstate_c.spline_type_u, gstate_c.spline_type_v); } + + if (dirtyUniforms & DIRTY_DEPAL) { + int indexMask = gstate.getClutIndexMask(); + int indexShift = gstate.getClutIndexShift(); + int indexOffset = gstate.getClutIndexStartPos() >> 4; + int format = gstate_c.depalFramebufferFormat; + uint32_t val = BytesToUint32(indexMask, indexShift, indexOffset, format); + // Poke in a bilinear filter flag in the top bit. + val |= gstate.isMagnifyFilteringEnabled() << 31; + ub->depal_mask_shift_off_fmt = val; + } } void LightUpdateUniforms(UB_VS_Lights *ub, uint64_t dirtyUniforms) { diff --git a/GPU/Common/ShaderUniforms.h b/GPU/Common/ShaderUniforms.h index a4c6d8f936..dd1e659604 100644 --- a/GPU/Common/ShaderUniforms.h +++ b/GPU/Common/ShaderUniforms.h @@ -11,7 +11,7 @@ enum : uint64_t { DIRTY_WORLDMATRIX | DIRTY_PROJTHROUGHMATRIX | DIRTY_VIEWMATRIX | DIRTY_TEXMATRIX | DIRTY_ALPHACOLORREF | DIRTY_PROJMATRIX | DIRTY_FOGCOLOR | DIRTY_FOGCOEF | DIRTY_TEXENV | DIRTY_STENCILREPLACEVALUE | DIRTY_ALPHACOLORMASK | DIRTY_SHADERBLEND | DIRTY_UVSCALEOFFSET | DIRTY_TEXCLAMP | DIRTY_DEPTHRANGE | DIRTY_MATAMBIENTALPHA | - DIRTY_BEZIERSPLINE, + DIRTY_BEZIERSPLINE | DIRTY_DEPAL, DIRTY_LIGHT_UNIFORMS = DIRTY_LIGHT0 | DIRTY_LIGHT1 | DIRTY_LIGHT2 | DIRTY_LIGHT3 | DIRTY_MATDIFFUSE | DIRTY_MATSPECULAR | DIRTY_MATEMISSIVE | DIRTY_AMBIENT, @@ -30,7 +30,8 @@ struct UB_VS_FS_Base { float depthRange[4]; float fogCoef[2]; float stencil; float pad0; float matAmbient[4]; - uint32_t spline_counts; int pad1; int pad2; int pad3; + uint32_t spline_counts; uint32_t depal_mask_shift_off_fmt; // 4 params packed into one. + int pad2; int pad3; // Fragment data float fogColor[4]; float texEnvColor[4]; @@ -54,7 +55,7 @@ R"( mat4 proj_mtx; float stencilReplace; vec4 matambientalpha; uint spline_counts; - int pad1; + uint depal_mask_shift_off_fmt; int pad2; int pad3; vec3 fogcolor; @@ -80,7 +81,7 @@ R"( float4x4 u_proj; float u_stencilReplaceValue; float4 u_matambientalpha; uint u_spline_counts; - int pad1; + uint u_depal_mask_shift_off_fmt; int pad2; int pad3; float3 u_fogcolor; diff --git a/GPU/GPUState.h b/GPU/GPUState.h index 58bc5e3859..e6724098c3 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -600,6 +600,9 @@ struct GPUStateCache { int spline_type_u; int spline_type_v; + bool useShaderDepal; + GEBufferFormat depalFramebufferFormat; + u32 getRelativeAddress(u32 data) const; void Reset(); void DoState(PointerWrap &p); diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index e8310abd18..2b360b9518 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -64,10 +64,11 @@ enum { VAI_KILL_AGE = 120, VAI_UNRELIABLE_KILL_AGE = 240, VAI_UNRELIABLE_KILL_MA enum { DRAW_BINDING_TEXTURE = 0, DRAW_BINDING_2ND_TEXTURE = 1, - DRAW_BINDING_DYNUBO_BASE = 2, - DRAW_BINDING_DYNUBO_LIGHT = 3, - DRAW_BINDING_DYNUBO_BONE = 4, - DRAW_BINDING_TESS_STORAGE_BUF = 5, + DRAW_BINDING_DEPAL_TEXTURE = 2, + DRAW_BINDING_DYNUBO_BASE = 3, + DRAW_BINDING_DYNUBO_LIGHT = 4, + DRAW_BINDING_DYNUBO_BONE = 5, + DRAW_BINDING_TESS_STORAGE_BUF = 6, }; enum { @@ -95,7 +96,7 @@ DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan, Draw::DrawContext *dra void DrawEngineVulkan::InitDeviceObjects() { // All resources we need for PSP drawing. Usually only bindings 0 and 2-4 are populated. - VkDescriptorSetLayoutBinding bindings[6]{}; + VkDescriptorSetLayoutBinding bindings[7]{}; bindings[0].descriptorCount = 1; bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; bindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; @@ -105,22 +106,26 @@ void DrawEngineVulkan::InitDeviceObjects() { bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; bindings[1].binding = DRAW_BINDING_2ND_TEXTURE; bindings[2].descriptorCount = 1; - bindings[2].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - bindings[2].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; - bindings[2].binding = DRAW_BINDING_DYNUBO_BASE; + bindings[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; // sampler is ignored though. + bindings[2].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + bindings[2].binding = DRAW_BINDING_DEPAL_TEXTURE; bindings[3].descriptorCount = 1; bindings[3].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - bindings[3].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - bindings[3].binding = DRAW_BINDING_DYNUBO_LIGHT; + bindings[3].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + bindings[3].binding = DRAW_BINDING_DYNUBO_BASE; bindings[4].descriptorCount = 1; bindings[4].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; bindings[4].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - bindings[4].binding = DRAW_BINDING_DYNUBO_BONE; - // Used only for hardware tessellation. + bindings[4].binding = DRAW_BINDING_DYNUBO_LIGHT; bindings[5].descriptorCount = 1; - bindings[5].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + bindings[5].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; bindings[5].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - bindings[5].binding = DRAW_BINDING_TESS_STORAGE_BUF; + bindings[5].binding = DRAW_BINDING_DYNUBO_BONE; + // Used only for hardware tessellation. + bindings[6].descriptorCount = 1; + bindings[6].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + bindings[6].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + bindings[6].binding = DRAW_BINDING_TESS_STORAGE_BUF; VkDevice device = vulkan_->GetDevice(); @@ -388,6 +393,7 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView key.imageView_ = imageView; key.sampler_ = sampler; key.secondaryImageView_ = boundSecondary_; + key.depalImageView_ = boundDepal_; key.base_ = base; key.light_ = light; key.bone_ = bone; @@ -433,12 +439,11 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView // Even in release mode, this is bad. _assert_msg_(G3D, result == VK_SUCCESS, "Ran out of descriptor space in pool. sz=%d res=%d", (int)frame.descSets.size(), (int)result); - // We just don't write to the slots we don't care about. - // We need 8 now that we support secondary texture bindings. - VkWriteDescriptorSet writes[8]{}; + // We just don't write to the slots we don't care about, which is fine. + VkWriteDescriptorSet writes[7]{}; // Main texture int n = 0; - VkDescriptorImageInfo tex[2]{}; + VkDescriptorImageInfo tex[3]{}; if (imageView) { #ifdef VULKAN_USE_GENERAL_LAYOUT_FOR_COLOR tex[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL; @@ -446,7 +451,7 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView tex[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; #endif tex[0].imageView = imageView; - tex[0].sampler = sampler; + tex[0].sampler = sampler; // We override sampling when doing depal. writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[n].pNext = nullptr; writes[n].dstBinding = DRAW_BINDING_TEXTURE; @@ -459,7 +464,7 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView if (boundSecondary_) { #ifdef VULKAN_USE_GENERAL_LAYOUT_FOR_COLOR - tex[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + tex[1].imageLayout = VK_IMAGE_LAYOUT_GENERAL; #else tex[1].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; #endif @@ -475,7 +480,23 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView n++; } - // Skipping 2nd texture for now. + if (boundDepal_) { +#ifdef VULKAN_USE_GENERAL_LAYOUT_FOR_COLOR + tex[2].imageLayout = VK_IMAGE_LAYOUT_GENERAL; +#else + tex[2].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; +#endif + tex[2].imageView = boundDepal_; + tex[2].sampler = samplerSecondary_; // doesn't matter, we use load + writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writes[n].pNext = nullptr; + writes[n].dstBinding = DRAW_BINDING_DEPAL_TEXTURE; + writes[n].pImageInfo = &tex[2]; + writes[n].descriptorCount = 1; + writes[n].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + writes[n].dstSet = desc; + n++; + } // Tessellation data buffer. Make sure this is declared outside the if to avoid optimizer // shenanigans. diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index 74cf5a0655..0aec77065e 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -20,10 +20,12 @@ // The Descriptor Set used for the majority of PSP rendering looks like this: // // * binding 0: Texture/Sampler (the PSP texture) -// * binding 1: Secondary texture sampler for shader blending or depal palettes -// * binding 2: Base Uniform Buffer (includes fragment state) -// * binding 3: Light uniform buffer -// * binding 4: Bone uniform buffer +// * binding 1: Secondary texture sampler for shader blending +// * binding 2: Depal palette +// * binding 3: Base Uniform Buffer (includes fragment state) +// * binding 4: Light uniform buffer +// * binding 5: Bone uniform buffer +// * binding 6: Tess data storage buffer // // All shaders conform to this layout, so they are all compatible with the same descriptor set. // The format of the various uniform buffers may vary though - vertex shaders that don't skin @@ -177,6 +179,9 @@ public: } void SetLineWidth(float lineWidth); + void SetDepalTexture(VkImageView depal) { + boundDepal_ = depal; + } private: struct FrameData; @@ -207,6 +212,7 @@ private: // Secondary texture for shader blending VkImageView boundSecondary_ = VK_NULL_HANDLE; + VkImageView boundDepal_ = VK_NULL_HANDLE; VkSampler samplerSecondary_ = VK_NULL_HANDLE; // This one is actually never used since we use fetch. PrehashMap vai_; @@ -217,6 +223,7 @@ private: struct DescriptorSetKey { VkImageView imageView_; VkImageView secondaryImageView_; + VkImageView depalImageView_; VkSampler sampler_; VkBuffer base_, light_, bone_; // All three UBO slots will be set to this. This will usually be identical // for all draws in a frame, except when the buffer has to grow. diff --git a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp index 38764ba7d3..7b2ee0b61c 100644 --- a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp @@ -60,6 +60,7 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { bool doTextureProjection = id.Bit(FS_BIT_DO_TEXTURE_PROJ); bool doTextureAlpha = id.Bit(FS_BIT_TEXALPHA); bool doFlatShading = id.Bit(FS_BIT_FLATSHADE); + bool shaderDepal = id.Bit(FS_BIT_SHADER_DEPAL); GEComparison alphaTestFunc = (GEComparison)id.Bits(FS_BIT_ALPHA_TEST_FUNC, 3); GEComparison colorTestFunc = (GEComparison)id.Bits(FS_BIT_COLOR_TEST_FUNC, 2); @@ -80,7 +81,7 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { const char *shading = doFlatShading ? "flat" : ""; - WRITE(p, "layout (std140, set = 0, binding = 2) uniform baseUBO {\n%s} base;\n", ub_baseStr); + WRITE(p, "layout (std140, set = 0, binding = 3) uniform baseUBO {\n%s} base;\n", ub_baseStr); if (doTexture) { WRITE(p, "layout (binding = 0) uniform sampler2D tex;\n"); } @@ -91,6 +92,10 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { } } + if (shaderDepal) { + WRITE(p, "layout (binding = 2) uniform sampler2D pal;\n"); + } + WRITE(p, "layout (location = 1) %s in vec4 v_color0;\n", shading); if (lmode) WRITE(p, "layout (location = 2) %s in vec3 v_color1;\n", shading); @@ -175,7 +180,39 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { } else { WRITE(p, " vec4 t = texture(tex, %s.xy);\n", texcoord); } - WRITE(p, " vec4 p = v_color0;\n"); + + if (shaderDepal) { + WRITE(p, " uint depalMask = (base.depal_mask_shift_off_fmt & 0xFF);\n"); + WRITE(p, " uint depalShift = (base.depal_mask_shift_off_fmt >> 8) & 0xFF;\n"); + WRITE(p, " uint depalOffset = ((base.depal_mask_shift_off_fmt >> 16) & 0xFF) << 4;\n"); + WRITE(p, " uint depalFmt = (base.depal_mask_shift_off_fmt >> 24) & 0x3;\n"); + WRITE(p, " bool bilinear = (base.depal_mask_shift_off_fmt >> 31) == 0;\n"); + WRITE(p, " vec2 fraction = fract(%s.xy);\n", texcoord); + WRITE(p, " uvec4 col; uint index0; uint index1; uint index2; uint index3;\n"); + WRITE(p, " switch (depalFmt) {\n"); // We might want to include fmt in the shader ID if this is a performance issue. + WRITE(p, " case 0:\n"); // 565 + WRITE(p, " col = uvec4(t.rgb * vec3(31.99, 63.99, 31.99), 0);\n"); + WRITE(p, " index0 = (col.b << 11) | (col.g << 5) | (col.r);\n"); + WRITE(p, " break;\n"); + WRITE(p, " case 1:\n"); // 5551 + WRITE(p, " col = uvec4(t.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n"); + WRITE(p, " index0 = (col.a << 15) | (col.b << 10) | (col.g << 5) | (col.r);\n"); + WRITE(p, " break;\n"); + WRITE(p, " case 2:\n"); // 4444 + WRITE(p, " col = uvec4(t.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n"); + WRITE(p, " index0 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n"); + WRITE(p, " break;\n"); + WRITE(p, " case 3:\n"); // 8888 + WRITE(p, " col = uvec4(t.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n"); + WRITE(p, " index0 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n"); + WRITE(p, " break;\n"); + WRITE(p, " };\n"); + WRITE(p, " index0 = ((index0 >> depalShift) & depalMask) | depalOffset;\n"); + WRITE(p, " t = texelFetch(pal, ivec2(index0, 0), 0);\n"); + } + + if (texFunc != GE_TEXFUNC_REPLACE || !doTextureAlpha) + WRITE(p, " vec4 p = v_color0;\n"); if (doTextureAlpha) { // texfmt == RGBA switch (texFunc) { diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 8834aa3717..c89b4f1db3 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -61,10 +61,9 @@ VulkanFragmentShader::VulkanFragmentShader(VulkanContext *vulkan, FShaderID id, } ERROR_LOG(G3D, "Messages: %s", errorMessage.c_str()); ERROR_LOG(G3D, "Shader source:\n%s", code); -#ifdef SHADERLOG + OutputDebugStringA(LineNumberString(code).c_str()); OutputDebugStringA("Messages:\n"); OutputDebugStringA(errorMessage.c_str()); -#endif Reporting::ReportMessage("Vulkan error in shader compilation: info: %s / code: %s", errorMessage.c_str(), code); } else { success = vulkan_->CreateShaderModule(spirv, &module_); @@ -116,6 +115,7 @@ VulkanVertexShader::VulkanVertexShader(VulkanContext *vulkan, VShaderID id, cons } ERROR_LOG(G3D, "Messages: %s", errorMessage.c_str()); ERROR_LOG(G3D, "Shader source:\n%s", code); + OutputDebugStringA(LineNumberString(code).c_str()); OutputDebugStringUTF8("Messages:\n"); OutputDebugStringUTF8(errorMessage.c_str()); Reporting::ReportMessage("Vulkan error in shader compilation: info: %s / code: %s", errorMessage.c_str(), code); @@ -354,7 +354,7 @@ VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderM // instantaneous. #define CACHE_HEADER_MAGIC 0xff51f420 -#define CACHE_VERSION 12 +#define CACHE_VERSION 13 struct VulkanCacheHeader { uint32_t magic; uint32_t version; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 6f1c14ad76..1e833ea939 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -327,6 +327,8 @@ void TextureCacheVulkan::BindTexture(TexCacheEntry *entry) { SamplerCacheKey key{}; UpdateSamplingParams(*entry, key); curSampler_ = samplerCache_.GetOrCreateSampler(key); + drawEngine_->SetDepalTexture(VK_NULL_HANDLE); + gstate_c.useShaderDepal = false; } void TextureCacheVulkan::Unbind() { @@ -336,10 +338,29 @@ void TextureCacheVulkan::Unbind() { } void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffer *framebuffer) { + SamplerCacheKey samplerKey{}; + SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight, samplerKey); + DepalShaderVulkan *depalShader = nullptr; uint32_t clutMode = gstate.clutformat & 0xFFFFFF; if ((entry->status & TexCacheEntry::STATUS_DEPALETTIZE) && !g_Config.bDisableSlowFramebufEffects) { - depalShader = depalShaderCache_->GetDepalettizeShader(clutMode, framebuffer->drawnFormat); + bool useShaderDepal = true; + if (useShaderDepal) { + depalShaderCache_->SetPushBuffer(drawEngine_->GetPushBufferForTextureData()); + const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); + VulkanTexture *clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_); + drawEngine_->SetDepalTexture(clutTexture->GetImageView()); + // Only point filtering enabled. + samplerKey.magFilt = false; + samplerKey.minFilt = false; + samplerKey.mipFilt = false; + // Make sure to update the uniforms. + gstate_c.Dirty(DIRTY_DEPAL); + gstate_c.useShaderDepal = true; + gstate_c.depalFramebufferFormat = framebuffer->drawnFormat; + } else { + depalShader = depalShaderCache_->GetDepalettizeShader(clutMode, framebuffer->drawnFormat); + } } if (depalShader) { depalShaderCache_->SetPushBuffer(drawEngine_->GetPushBufferForTextureData()); @@ -430,14 +451,12 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr } else { entry->status &= ~TexCacheEntry::STATUS_DEPALETTIZE; - framebufferManager_->RebindFramebuffer(); // TODO: This line should not be needed? + framebufferManager_->RebindFramebuffer(); // TODO: This line should usually not be needed. imageView_ = framebufferManagerVulkan_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET); gstate_c.SetTextureFullAlpha(gstate.getTextureFormat() == GE_TFMT_5650); } - SamplerCacheKey samplerKey{}; - SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight, samplerKey); curSampler_ = samplerCache_.GetOrCreateSampler(samplerKey); InvalidateLastTexture(entry); } diff --git a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp index d72d8c904a..b4a22977fa 100644 --- a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp @@ -137,11 +137,11 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) { bool flipNormalTess = id.Bit(VS_BIT_NORM_REVERSE_TESS); WRITE(p, "\n"); - WRITE(p, "layout (std140, set = 0, binding = 2) uniform baseVars {\n%s} base;\n", ub_baseStr); + WRITE(p, "layout (std140, set = 0, binding = 3) uniform baseVars {\n%s} base;\n", ub_baseStr); if (enableLighting || doShadeMapping) - WRITE(p, "layout (std140, set = 0, binding = 3) uniform lightVars {\n%s} light;\n", ub_vs_lightsStr); + WRITE(p, "layout (std140, set = 0, binding = 4) uniform lightVars {\n%s} light;\n", ub_vs_lightsStr); if (enableBones) - WRITE(p, "layout (std140, set = 0, binding = 4) uniform boneVars {\n%s} bone;\n", ub_vs_bonesStr); + WRITE(p, "layout (std140, set = 0, binding = 5) uniform boneVars {\n%s} bone;\n", ub_vs_bonesStr); const char *shading = doFlatShading ? "flat " : ""; @@ -221,7 +221,7 @@ bool GenerateVulkanGLSLVertexShader(const VShaderID &id, char *buffer) { WRITE(p, " vec4 uv;\n"); WRITE(p, " vec4 color;\n"); WRITE(p, "};"); - WRITE(p, "layout (std430, set = 0, binding = 5) buffer s_tess_data {\n"); + WRITE(p, "layout (std430, set = 0, binding = 6) buffer s_tess_data {\n"); WRITE(p, " TessData data[];"); WRITE(p, "} tess_data;\n"); From 69bd427ca2ea1d3e105484f903376b936a07e445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 13 Apr 2018 12:45:10 +0200 Subject: [PATCH 34/40] Shader depal: Implement bilinear filtering. --- GPU/GPUCommon.cpp | 2 +- GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp | 55 +++++++++++++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 713e4c4120..262ed460a7 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -161,7 +161,7 @@ const CommonCommandTableEntry commonCommandTable[] = { // These must flush on change, so that LoadClut doesn't have to always flush. { GE_CMD_CLUTADDR, FLAG_FLUSHBEFOREONCHANGE }, { GE_CMD_CLUTADDRUPPER, FLAG_FLUSHBEFOREONCHANGE }, - { GE_CMD_CLUTFORMAT, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, + { GE_CMD_CLUTFORMAT, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS | DIRTY_DEPAL }, // Morph weights. TODO: Remove precomputation? { GE_CMD_MORPHWEIGHT0, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_MorphWeight }, diff --git a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp index 7b2ee0b61c..08327ec747 100644 --- a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp @@ -177,8 +177,18 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { if (doTextureProjection) { WRITE(p, " vec4 t = textureProj(tex, %s);\n", texcoord); + if (shaderDepal) { + WRITE(p, " vec4 t1 = textureProjOffset(tex, %s, ivec2(1, 0));\n", texcoord); + WRITE(p, " vec4 t2 = textureProjOffset(tex, %s, ivec2(0, 1));\n", texcoord); + WRITE(p, " vec4 t3 = textureProjOffset(tex, %s, ivec2(1, 1));\n", texcoord); + } } else { WRITE(p, " vec4 t = texture(tex, %s.xy);\n", texcoord); + if (shaderDepal) { + WRITE(p, " vec4 t1 = textureOffset(tex, %s.xy, ivec2(1, 0));\n", texcoord); + WRITE(p, " vec4 t2 = textureOffset(tex, %s.xy, ivec2(0, 1));\n", texcoord); + WRITE(p, " vec4 t3 = textureOffset(tex, %s.xy, ivec2(1, 1));\n", texcoord); + } } if (shaderDepal) { @@ -186,29 +196,72 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { WRITE(p, " uint depalShift = (base.depal_mask_shift_off_fmt >> 8) & 0xFF;\n"); WRITE(p, " uint depalOffset = ((base.depal_mask_shift_off_fmt >> 16) & 0xFF) << 4;\n"); WRITE(p, " uint depalFmt = (base.depal_mask_shift_off_fmt >> 24) & 0x3;\n"); - WRITE(p, " bool bilinear = (base.depal_mask_shift_off_fmt >> 31) == 0;\n"); + WRITE(p, " bool bilinear = (base.depal_mask_shift_off_fmt >> 31) != 0;\n"); WRITE(p, " vec2 fraction = fract(%s.xy);\n", texcoord); WRITE(p, " uvec4 col; uint index0; uint index1; uint index2; uint index3;\n"); WRITE(p, " switch (depalFmt) {\n"); // We might want to include fmt in the shader ID if this is a performance issue. WRITE(p, " case 0:\n"); // 565 WRITE(p, " col = uvec4(t.rgb * vec3(31.99, 63.99, 31.99), 0);\n"); WRITE(p, " index0 = (col.b << 11) | (col.g << 5) | (col.r);\n"); + WRITE(p, " if (bilinear) {\n"); + WRITE(p, " col = uvec4(t1.rgb * vec3(31.99, 63.99, 31.99), 0);\n"); + WRITE(p, " index1 = (col.b << 11) | (col.g << 5) | (col.r);\n"); + WRITE(p, " col = uvec4(t2.rgb * vec3(31.99, 63.99, 31.99), 0);\n"); + WRITE(p, " index2 = (col.b << 11) | (col.g << 5) | (col.r);\n"); + WRITE(p, " col = uvec4(t3.rgb * vec3(31.99, 63.99, 31.99), 0);\n"); + WRITE(p, " index3 = (col.b << 11) | (col.g << 5) | (col.r);\n"); + WRITE(p, " }\n"); WRITE(p, " break;\n"); WRITE(p, " case 1:\n"); // 5551 WRITE(p, " col = uvec4(t.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n"); WRITE(p, " index0 = (col.a << 15) | (col.b << 10) | (col.g << 5) | (col.r);\n"); + WRITE(p, " if (bilinear) {\n"); + WRITE(p, " col = uvec4(t1.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n"); + WRITE(p, " index1 = (col.a << 15) | (col.b << 10) | (col.g << 5) | (col.r);\n"); + WRITE(p, " col = uvec4(t2.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n"); + WRITE(p, " index2 = (col.a << 15) | (col.b << 10) | (col.g << 5) | (col.r);\n"); + WRITE(p, " col = uvec4(t3.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n"); + WRITE(p, " index3 = (col.a << 15) | (col.b << 10) | (col.g << 5) | (col.r);\n"); + WRITE(p, " }\n"); WRITE(p, " break;\n"); WRITE(p, " case 2:\n"); // 4444 WRITE(p, " col = uvec4(t.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n"); WRITE(p, " index0 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n"); + WRITE(p, " if (bilinear) {\n"); + WRITE(p, " col = uvec4(t1.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n"); + WRITE(p, " index1 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n"); + WRITE(p, " col = uvec4(t2.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n"); + WRITE(p, " index2 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n"); + WRITE(p, " col = uvec4(t3.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n"); + WRITE(p, " index3 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n"); + WRITE(p, " }\n"); WRITE(p, " break;\n"); WRITE(p, " case 3:\n"); // 8888 WRITE(p, " col = uvec4(t.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n"); WRITE(p, " index0 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n"); + WRITE(p, " if (bilinear) {\n"); + WRITE(p, " col = uvec4(t1.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n"); + WRITE(p, " index1 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n"); + WRITE(p, " col = uvec4(t2.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n"); + WRITE(p, " index2 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n"); + WRITE(p, " col = uvec4(t3.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n"); + WRITE(p, " index3 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n"); + WRITE(p, " }\n"); WRITE(p, " break;\n"); WRITE(p, " };\n"); WRITE(p, " index0 = ((index0 >> depalShift) & depalMask) | depalOffset;\n"); WRITE(p, " t = texelFetch(pal, ivec2(index0, 0), 0);\n"); + WRITE(p, " if (bilinear) {\n"); + WRITE(p, " index1 = ((index1 >> depalShift) & depalMask) | depalOffset;\n"); + WRITE(p, " index2 = ((index2 >> depalShift) & depalMask) | depalOffset;\n"); + WRITE(p, " index3 = ((index3 >> depalShift) & depalMask) | depalOffset;\n"); + WRITE(p, " t1 = texelFetch(pal, ivec2(index1, 0), 0);\n"); + WRITE(p, " t2 = texelFetch(pal, ivec2(index2, 0), 0);\n"); + WRITE(p, " t3 = texelFetch(pal, ivec2(index3, 0), 0);\n"); + WRITE(p, " t = mix(t, t1, fraction.x);\n"); + WRITE(p, " t2 = mix(t2, t3, fraction.x);\n"); + WRITE(p, " t = mix(t, t2, fraction.y);\n"); + WRITE(p, " }\n"); } if (texFunc != GE_TEXFUNC_REPLACE || !doTextureAlpha) From 81276c88626985136dd05c6fc10bfdea90b57517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 13 Apr 2018 13:47:45 +0200 Subject: [PATCH 35/40] Fix various bugs. --- GPU/Vulkan/DrawEngineVulkan.cpp | 2 +- GPU/Vulkan/ShaderManagerVulkan.cpp | 4 ++++ GPU/Vulkan/TextureCacheVulkan.cpp | 13 +++++++++++-- GPU/Vulkan/TextureCacheVulkan.h | 1 + 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 2b360b9518..963898d26c 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -451,7 +451,7 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView tex[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; #endif tex[0].imageView = imageView; - tex[0].sampler = sampler; // We override sampling when doing depal. + tex[0].sampler = sampler; writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[n].pNext = nullptr; writes[n].dstBinding = DRAW_BINDING_TEXTURE; diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index c89b4f1db3..7b2aeae038 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -61,9 +61,11 @@ VulkanFragmentShader::VulkanFragmentShader(VulkanContext *vulkan, FShaderID id, } ERROR_LOG(G3D, "Messages: %s", errorMessage.c_str()); ERROR_LOG(G3D, "Shader source:\n%s", code); +#ifdef SHADERLOG OutputDebugStringA(LineNumberString(code).c_str()); OutputDebugStringA("Messages:\n"); OutputDebugStringA(errorMessage.c_str()); +#endif Reporting::ReportMessage("Vulkan error in shader compilation: info: %s / code: %s", errorMessage.c_str(), code); } else { success = vulkan_->CreateShaderModule(spirv, &module_); @@ -115,9 +117,11 @@ VulkanVertexShader::VulkanVertexShader(VulkanContext *vulkan, VShaderID id, cons } ERROR_LOG(G3D, "Messages: %s", errorMessage.c_str()); ERROR_LOG(G3D, "Shader source:\n%s", code); +#ifdef SHADERLOG OutputDebugStringA(LineNumberString(code).c_str()); OutputDebugStringUTF8("Messages:\n"); OutputDebugStringUTF8(errorMessage.c_str()); +#endif Reporting::ReportMessage("Vulkan error in shader compilation: info: %s / code: %s", errorMessage.c_str(), code); } else { success = vulkan_->CreateShaderModule(spirv, &module_); diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 1e833ea939..a2f350596a 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -343,8 +343,10 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr DepalShaderVulkan *depalShader = nullptr; uint32_t clutMode = gstate.clutformat & 0xFFFFFF; + + bool useShaderDepal = true; + if ((entry->status & TexCacheEntry::STATUS_DEPALETTIZE) && !g_Config.bDisableSlowFramebufEffects) { - bool useShaderDepal = true; if (useShaderDepal) { depalShaderCache_->SetPushBuffer(drawEngine_->GetPushBufferForTextureData()); const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); @@ -358,6 +360,14 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr gstate_c.Dirty(DIRTY_DEPAL); gstate_c.useShaderDepal = true; gstate_c.depalFramebufferFormat = framebuffer->drawnFormat; + const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16); + const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor; + TexCacheEntry::TexStatus alphaStatus = CheckAlpha(clutBuf_, getClutDestFormatVulkan(clutFormat), clutTotalColors, clutTotalColors, 1); + gstate_c.SetTextureFullAlpha(alphaStatus == TexCacheEntry::STATUS_ALPHA_FULL); + curSampler_ = samplerCache_.GetOrCreateSampler(samplerKey); + InvalidateLastTexture(entry); + imageView_ = framebufferManagerVulkan_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET); + return; } else { depalShader = depalShaderCache_->GetDepalettizeShader(clutMode, framebuffer->drawnFormat); } @@ -456,7 +466,6 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFr gstate_c.SetTextureFullAlpha(gstate.getTextureFormat() == GE_TFMT_5650); } - curSampler_ = samplerCache_.GetOrCreateSampler(samplerKey); InvalidateLastTexture(entry); } diff --git a/GPU/Vulkan/TextureCacheVulkan.h b/GPU/Vulkan/TextureCacheVulkan.h index 3ad62fc74d..77f55aaea8 100644 --- a/GPU/Vulkan/TextureCacheVulkan.h +++ b/GPU/Vulkan/TextureCacheVulkan.h @@ -86,6 +86,7 @@ public: lastBoundTexture = nullptr; gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); } + void InvalidateLastTexture(TexCacheEntry *entry = nullptr) override { if (!entry || entry->vkTex == lastBoundTexture) { lastBoundTexture = nullptr; From 413a204138fac07d2eb99c1b519674cd8a03d6d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 13 Apr 2018 17:32:36 +0200 Subject: [PATCH 36/40] Vulkan: Semi-gross hack that massively improves the perf of MGS2:Acid. --- Core/Compatibility.cpp | 1 + Core/Compatibility.h | 1 + GPU/Vulkan/DrawEngineVulkan.cpp | 2 +- GPU/Vulkan/GPU_Vulkan.cpp | 15 ++++++ assets/compat.ini | 10 +++- ext/native/thin3d/GLRenderManager.cpp | 2 +- ext/native/thin3d/VulkanQueueRunner.cpp | 68 ++++++++++++++++++++++++- ext/native/thin3d/VulkanQueueRunner.h | 17 ++++++- 8 files changed, 111 insertions(+), 5 deletions(-) diff --git a/Core/Compatibility.cpp b/Core/Compatibility.cpp index b12daff7b3..2fed762c0d 100644 --- a/Core/Compatibility.cpp +++ b/Core/Compatibility.cpp @@ -57,6 +57,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) { CheckSetting(iniFile, gameID, "RequireDefaultCPUClock", &flags_.RequireDefaultCPUClock); CheckSetting(iniFile, gameID, "DisableReadbacks", &flags_.DisableReadbacks); CheckSetting(iniFile, gameID, "DisableAccurateDepth", &flags_.DisableAccurateDepth); + CheckSetting(iniFile, gameID, "MGS2AcidHack", &flags_.MGS2AcidHack); } void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) { diff --git a/Core/Compatibility.h b/Core/Compatibility.h index 78cc86db12..6f60504a40 100644 --- a/Core/Compatibility.h +++ b/Core/Compatibility.h @@ -57,6 +57,7 @@ struct CompatFlags { bool RequireDefaultCPUClock; bool DisableReadbacks; bool DisableAccurateDepth; + bool MGS2AcidHack; }; class IniFile; diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 963898d26c..7a5eeb3f0c 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -372,7 +372,7 @@ VkResult DrawEngineVulkan::RecreateDescriptorPool(FrameData &frame, int newSize) VkDescriptorPoolSize dpTypes[3]; dpTypes[0].descriptorCount = frame.descPoolSize * 3; dpTypes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - dpTypes[1].descriptorCount = frame.descPoolSize * 2; // Don't use these for tess anymore, need max two per set. + dpTypes[1].descriptorCount = frame.descPoolSize * 3; // Don't use these for tess anymore, need max three per set. dpTypes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; dpTypes[2].descriptorCount = frame.descPoolSize; dpTypes[2].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index b5d78c9595..e69bf7454a 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -42,6 +42,8 @@ #include "GPU/Vulkan/FramebufferVulkan.h" #include "GPU/Vulkan/DrawEngineVulkan.h" #include "GPU/Vulkan/TextureCacheVulkan.h" +#include "thin3d/VulkanRenderManager.h" +#include "thin3d/VulkanQueueRunner.h" #include "Core/MIPS/MIPS.h" #include "Core/HLE/sceKernelThread.h" @@ -456,6 +458,15 @@ void GPU_Vulkan::InitDeviceObjects() { assert(!frameData_[i].push_); frameData_[i].push_ = new VulkanPushBuffer(vulkan_, 64 * 1024); } + + VulkanRenderManager *rm = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + uint32_t hacks = 0; + if (PSP_CoreParameter().compat.flags().MGS2AcidHack) { + hacks |= QUEUE_HACK_MGS2_ACID; + } + if (hacks) { + rm->GetQueueRunner()->EnableHacks(hacks); + } } void GPU_Vulkan::DestroyDeviceObjects() { @@ -467,6 +478,10 @@ void GPU_Vulkan::DestroyDeviceObjects() { frameData_[i].push_ = nullptr; } } + + // Need to turn off hacks when shutting down the GPU. Don't want them running in the menu. + VulkanRenderManager *rm = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); + rm->GetQueueRunner()->EnableHacks(0); } void GPU_Vulkan::DeviceLost() { diff --git a/assets/compat.ini b/assets/compat.ini index e977c75b48..8f9d668b05 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -325,4 +325,12 @@ NPHG00092 = true NPEG00044 = true NPJG00120 = true UCJS10114 = true -UCES01401 = true \ No newline at end of file +UCES01401 = true + +[MGS2AcidHack] +ULES00008 = true +ULJM08001 = true +ULJM05001 = true +ULAS42007 = true +ULUS10006 = true +ULUS10077 = true diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 32936bd4be..1e986b4195 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -472,6 +472,7 @@ void GLRenderManager::Run(int frame) { auto &initStepsOnThread = frameData_[frame].initSteps; // queueRunner_.LogSteps(stepsOnThread); queueRunner_.RunInitSteps(initStepsOnThread); + initStepsOnThread.clear(); // Run this after RunInitSteps so any fresh GLRBuffers for the pushbuffers can get created. for (auto iter : frameData.activePushBuffers) { @@ -481,7 +482,6 @@ void GLRenderManager::Run(int frame) { queueRunner_.RunSteps(stepsOnThread); stepsOnThread.clear(); - initStepsOnThread.clear(); for (auto iter : frameData.activePushBuffers) { iter->MapDevice(bufferStrategy_); diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 6994c96bff..465159ddc7 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -347,7 +347,7 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(const RPKey &key) { return pass; } -void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, const std::vector &steps) { +void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &steps) { // Optimizes renderpasses, then sequences them. // Planned optimizations: // * Create copies of render target that are rendered to multiple times and textured from in sequence, and push those render passes @@ -397,6 +397,12 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, const std::vector &steps) { + // We want to turn a sequence of copy,render(1),copy,render(1),copy,render(1) to copy,copy,copy,render(n). + + for (int i = 0; i < (int)steps.size() - 3; i++) { + int last = -1; + if (!(steps[i]->stepType == VKRStepType::COPY && + steps[i + 1]->stepType == VKRStepType::RENDER && + steps[i + 2]->stepType == VKRStepType::COPY && + steps[i + 1]->render.numDraws == 1 && + steps[i]->copy.dst == steps[i + 2]->copy.dst)) + continue; + // Looks promising! Let's start by finding the last one. + for (int j = i; j < (int)steps.size(); j++) { + switch (steps[j]->stepType) { + case VKRStepType::RENDER: + if (steps[j]->render.numDraws > 1) + last = j - 1; + break; + case VKRStepType::COPY: + if (steps[j]->copy.dst != steps[i]->copy.dst) + last = j - 1; + break; + } + if (last != -1) + break; + } + + if (last != -1) { + // We've got a sequence from i to last that needs reordering. + // First, let's sort it, keeping the same length. + std::vector copies; + std::vector renders; + for (int n = i; n <= last; n++) { + if (steps[n]->stepType == VKRStepType::COPY) + copies.push_back(steps[n]); + else if (steps[n]->stepType == VKRStepType::RENDER) + renders.push_back(steps[n]); + } + // Write the copies back. TODO: Combine them too. + for (int j = 0; j < (int)copies.size(); j++) { + steps[i + j] = copies[j]; + } + // Write the renders back (so they will be deleted properly). + for (int j = 0; j < (int)renders.size(); j++) { + steps[i + j + copies.size()] = renders[j]; + } + assert(steps[i + j + copies.size()]->stepType == VKRStepType::RENDER); + // Combine the renders. + for (int j = 1; j < (int)renders.size(); j++) { + for (int k = 0; k < renders[j]->commands.size(); k++) { + steps[i + copies.size()]->commands.push_back(renders[j]->commands[k]); + } + steps[i + copies.size() + j]->stepType = VKRStepType::RENDER_SKIP; + } + // We're done. + break; + } + } +} + void VulkanQueueRunner::LogSteps(const std::vector &steps) { ILOG("======================================="); for (size_t i = 0; i < steps.size(); i++) { diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index 0dd3ba8597..e7f6df1fa2 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -10,6 +10,10 @@ class VKRFramebuffer; struct VKRImage; +enum { + QUEUE_HACK_MGS2_ACID = 1, +}; + enum class VKRRenderCommand : uint8_t { BIND_PIPELINE, STENCIL, @@ -152,7 +156,9 @@ public: backbuffer_ = fb; backbufferImage_ = img; } - void RunSteps(VkCommandBuffer cmd, const std::vector &steps); + + // RunSteps can modify steps but will leave it in a valid state. + void RunSteps(VkCommandBuffer cmd, std::vector &steps); void LogSteps(const std::vector &steps); void CreateDeviceObjects(); @@ -205,6 +211,10 @@ public: return found; } + void EnableHacks(uint32_t hacks) { + hacksEnabled_ = hacks; + } + private: void InitBackbufferRenderPass(); @@ -223,6 +233,8 @@ private: void ResizeReadbackBuffer(VkDeviceSize requiredSize); + void ApplyMGSHack(std::vector &steps); + static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); @@ -244,4 +256,7 @@ private: VkDeviceMemory readbackMemory_ = VK_NULL_HANDLE; VkBuffer readbackBuffer_ = VK_NULL_HANDLE; VkDeviceSize readbackBufferSize_ = 0; + + // TODO: Enable based on compat.ini. + uint32_t hacksEnabled_ = 0; }; From 0ac6cea34dd7f1594c3f9e9bf8c69e6f1300f79b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 13 Apr 2018 18:05:04 +0200 Subject: [PATCH 37/40] Add a queue processing hack for Sonic Rivals too. Now it's fast. --- Core/Compatibility.cpp | 1 + Core/Compatibility.h | 1 + GPU/Vulkan/GPU_Vulkan.cpp | 5 +- assets/compat.ini | 4 ++ ext/native/thin3d/VulkanQueueRunner.cpp | 93 +++++++++++++++++++++++-- ext/native/thin3d/VulkanQueueRunner.h | 2 + 6 files changed, 100 insertions(+), 6 deletions(-) diff --git a/Core/Compatibility.cpp b/Core/Compatibility.cpp index 2fed762c0d..1cc0ccb219 100644 --- a/Core/Compatibility.cpp +++ b/Core/Compatibility.cpp @@ -58,6 +58,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) { CheckSetting(iniFile, gameID, "DisableReadbacks", &flags_.DisableReadbacks); CheckSetting(iniFile, gameID, "DisableAccurateDepth", &flags_.DisableAccurateDepth); CheckSetting(iniFile, gameID, "MGS2AcidHack", &flags_.MGS2AcidHack); + CheckSetting(iniFile, gameID, "SonicRivalsHack", &flags_.SonicRivalsHack); } void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) { diff --git a/Core/Compatibility.h b/Core/Compatibility.h index 6f60504a40..f65c68efda 100644 --- a/Core/Compatibility.h +++ b/Core/Compatibility.h @@ -58,6 +58,7 @@ struct CompatFlags { bool DisableReadbacks; bool DisableAccurateDepth; bool MGS2AcidHack; + bool SonicRivalsHack; }; class IniFile; diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index e69bf7454a..e774e735bc 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -461,9 +461,10 @@ void GPU_Vulkan::InitDeviceObjects() { VulkanRenderManager *rm = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); uint32_t hacks = 0; - if (PSP_CoreParameter().compat.flags().MGS2AcidHack) { + if (PSP_CoreParameter().compat.flags().MGS2AcidHack) hacks |= QUEUE_HACK_MGS2_ACID; - } + if (PSP_CoreParameter().compat.flags().SonicRivalsHack) + hacks |= QUEUE_HACK_SONIC; if (hacks) { rm->GetQueueRunner()->EnableHacks(hacks); } diff --git a/assets/compat.ini b/assets/compat.ini index 8f9d668b05..6d0de1a99f 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -334,3 +334,7 @@ ULJM05001 = true ULAS42007 = true ULUS10006 = true ULUS10077 = true + +[SonicRivalsHack] +ULES00622 = true +ULUS10195 = true diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 465159ddc7..eba6f52dc2 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -398,9 +398,14 @@ void VulkanQueueRunner::RunSteps(VkCommandBuffer cmd, std::vector &st } // Queue hacks. - if (hacksEnabled_ & QUEUE_HACK_MGS2_ACID) { - // Massive speedup. - ApplyMGSHack(steps); + if (hacksEnabled_) { + if (hacksEnabled_ & QUEUE_HACK_MGS2_ACID) { + // Massive speedup. + ApplyMGSHack(steps); + } + if (hacksEnabled_ & QUEUE_HACK_SONIC) { + ApplySonicHack(steps); + } } for (size_t i = 0; i < steps.size(); i++) { @@ -460,6 +465,8 @@ void VulkanQueueRunner::ApplyMGSHack(std::vector &steps) { // First, let's sort it, keeping the same length. std::vector copies; std::vector renders; + copies.reserve((last - i) / 2); + renders.reserve((last - i) / 2); for (int n = i; n <= last; n++) { if (steps[n]->stepType == VKRStepType::COPY) copies.push_back(steps[n]); @@ -474,7 +481,7 @@ void VulkanQueueRunner::ApplyMGSHack(std::vector &steps) { for (int j = 0; j < (int)renders.size(); j++) { steps[i + j + copies.size()] = renders[j]; } - assert(steps[i + j + copies.size()]->stepType == VKRStepType::RENDER); + assert(steps[i + copies.size()]->stepType == VKRStepType::RENDER); // Combine the renders. for (int j = 1; j < (int)renders.size(); j++) { for (int k = 0; k < renders[j]->commands.size(); k++) { @@ -488,6 +495,84 @@ void VulkanQueueRunner::ApplyMGSHack(std::vector &steps) { } } +void VulkanQueueRunner::ApplySonicHack(std::vector &steps) { + // We want to turn a sequence of render(3),render(1),render(6),render(1),render(6),render(1),render(3) to + // render(1), render(1), render(1), render(6), render(6), render(6) + + for (int i = 0; i < (int)steps.size() - 4; i++) { + int last = -1; + if (!(steps[i]->stepType == VKRStepType::RENDER && + steps[i + 1]->stepType == VKRStepType::RENDER && + steps[i + 2]->stepType == VKRStepType::RENDER && + steps[i + 3]->stepType == VKRStepType::RENDER && + steps[i]->render.numDraws == 3 && + steps[i + 1]->render.numDraws == 1 && + steps[i + 2]->render.numDraws == 6 && + steps[i + 3]->render.numDraws == 1 && + steps[i]->render.framebuffer == steps[i + 2]->render.framebuffer && + steps[i + 1]->render.framebuffer == steps[i + 3]->render.framebuffer)) + continue; + // Looks promising! Let's start by finding the last one. + for (int j = i; j < (int)steps.size(); j++) { + switch (steps[j]->stepType) { + case VKRStepType::RENDER: + if ((j - i) & 1) { + if (steps[j]->render.framebuffer != steps[i + 1]->render.framebuffer) + last = j - 1; + if (steps[j]->render.numDraws != 1) + last = j - 1; + } else { + if (steps[j]->render.framebuffer != steps[i]->render.framebuffer) + last = j - 1; + if (steps[j]->render.numDraws != 3 && steps[j]->render.numDraws != 6) + last = j - 1; + } + } + if (last != -1) + break; + } + + if (last != -1) { + // We've got a sequence from i to last that needs reordering. + // First, let's sort it, keeping the same length. + std::vector type1; + std::vector type2; + type1.reserve((last - i) / 2); + type2.reserve((last - i) / 2); + for (int n = i; n <= last; n++) { + if (steps[n]->render.framebuffer == steps[i]->render.framebuffer) + type1.push_back(steps[n]); + else + type2.push_back(steps[n]); + } + + // Write the renders back in order. Same amount, so deletion will work fine. + for (int j = 0; j < (int)type1.size(); j++) { + steps[i + j] = type1[j]; + } + for (int j = 0; j < (int)type2.size(); j++) { + steps[i + j + type1.size()] = type2[j]; + } + + // Combine the renders. + for (int j = 1; j < (int)type1.size(); j++) { + for (int k = 0; k < (int)type1[j]->commands.size(); k++) { + steps[i]->commands.push_back(type1[j]->commands[k]); + } + steps[i + j]->stepType = VKRStepType::RENDER_SKIP; + } + for (int j = 1; j < (int)type2.size(); j++) { + for (int k = 0; k < (int)type2[j]->commands.size(); k++) { + steps[i + type1.size()]->commands.push_back(type2[j]->commands[k]); + } + steps[i + j + type1.size()]->stepType = VKRStepType::RENDER_SKIP; + } + // We're done. + break; + } + } +} + void VulkanQueueRunner::LogSteps(const std::vector &steps) { ILOG("======================================="); for (size_t i = 0; i < steps.size(); i++) { diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index e7f6df1fa2..d743bda730 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -12,6 +12,7 @@ struct VKRImage; enum { QUEUE_HACK_MGS2_ACID = 1, + QUEUE_HACK_SONIC = 2, }; enum class VKRRenderCommand : uint8_t { @@ -234,6 +235,7 @@ private: void ResizeReadbackBuffer(VkDeviceSize requiredSize); void ApplyMGSHack(std::vector &steps); + void ApplySonicHack(std::vector &steps); static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect); From 0479255f76855b410bf4e62bee5515317b4dde05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 13 Apr 2018 18:28:38 +0200 Subject: [PATCH 38/40] Let's try it on SR2 as well. --- assets/compat.ini | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/assets/compat.ini b/assets/compat.ini index 6d0de1a99f..72ac34b4c9 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -336,5 +336,7 @@ ULUS10006 = true ULUS10077 = true [SonicRivalsHack] -ULES00622 = true -ULUS10195 = true +ULES00622 = true # SR1 +ULUS10195 = true # SR1 +ULUS10323 = true # SR2 +ULES00940 = true # SR2 \ No newline at end of file From fb7a63bd11ba785209babfe9631956110d46c9bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 13 Apr 2018 20:00:14 +0200 Subject: [PATCH 39/40] Implement shader depal for GL as well, but disabled by default. --- GPU/GLES/FragmentShaderGeneratorGLES.cpp | 94 +++++++++++++++++++++++- GPU/GLES/ShaderManagerGLES.cpp | 16 +++- GPU/GLES/ShaderManagerGLES.h | 4 + GPU/GLES/TextureCacheGLES.cpp | 35 ++++++++- GPU/GLES/TextureCacheGLES.h | 2 +- ext/native/thin3d/GLQueueRunner.cpp | 6 +- 6 files changed, 150 insertions(+), 7 deletions(-) diff --git a/GPU/GLES/FragmentShaderGeneratorGLES.cpp b/GPU/GLES/FragmentShaderGeneratorGLES.cpp index 913e6b5829..d8b117601f 100644 --- a/GPU/GLES/FragmentShaderGeneratorGLES.cpp +++ b/GPU/GLES/FragmentShaderGeneratorGLES.cpp @@ -157,6 +157,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform bool doTextureProjection = id.Bit(FS_BIT_DO_TEXTURE_PROJ); bool doTextureAlpha = id.Bit(FS_BIT_TEXALPHA); bool doFlatShading = id.Bit(FS_BIT_FLATSHADE); + bool shaderDepal = id.Bit(FS_BIT_SHADER_DEPAL); GEComparison alphaTestFunc = (GEComparison)id.Bits(FS_BIT_ALPHA_TEST_FUNC, 3); GEComparison colorTestFunc = (GEComparison)id.Bits(FS_BIT_COLOR_TEST_FUNC, 2); @@ -217,6 +218,12 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform } } + if (shaderDepal) { + WRITE(p, "uniform sampler2D pal;\n"); + WRITE(p, "uniform int u_depal;\n"); + *uniformMask |= DIRTY_DEPAL; + } + StencilValueType replaceAlphaWithStencilType = (StencilValueType)id.Bits(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE, 4); if (stencilToAlpha && replaceAlphaWithStencilType == STENCIL_VALUE_UNIFORM) { *uniformMask |= DIRTY_STENCILREPLACEVALUE; @@ -336,10 +343,95 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform if (doTextureProjection) { WRITE(p, " vec4 t = %sProj(tex, %s);\n", texture, texcoord); + if (shaderDepal) { + WRITE(p, " vec4 t1 = %sProjOffset(tex, %s, ivec2(1, 0));\n", texture, texcoord); + WRITE(p, " vec4 t2 = %sProjOffset(tex, %s, ivec2(0, 1));\n", texture, texcoord); + WRITE(p, " vec4 t3 = %sProjOffset(tex, %s, ivec2(1, 1));\n", texture, texcoord); + } } else { WRITE(p, " vec4 t = %s(tex, %s.xy);\n", texture, texcoord); + if (shaderDepal) { + WRITE(p, " vec4 t1 = %sOffset(tex, %s.xy, ivec2(1, 0));\n", texture, texcoord); + WRITE(p, " vec4 t2 = %sOffset(tex, %s.xy, ivec2(0, 1));\n", texture, texcoord); + WRITE(p, " vec4 t3 = %sOffset(tex, %s.xy, ivec2(1, 1));\n", texture, texcoord); + } } - WRITE(p, " vec4 p = v_color0;\n"); + + if (shaderDepal) { + WRITE(p, " int depalMask = (u_depal & 0xFF);\n"); + WRITE(p, " int depalShift = ((u_depal >> 8) & 0xFF);\n"); + WRITE(p, " int depalOffset = (((u_depal >> 16) & 0xFF) << 4);\n"); + WRITE(p, " int depalFmt = ((u_depal >> 24) & 0x3);\n"); + WRITE(p, " bool bilinear = (u_depal >> 31) != 0;\n"); + WRITE(p, " vec2 fraction = fract(%s.xy);\n", texcoord); + WRITE(p, " ivec4 col; int index0; int index1; int index2; int index3;\n"); + WRITE(p, " switch (depalFmt) {\n"); // We might want to include fmt in the shader ID if this is a performance issue. + WRITE(p, " case 0:\n"); // 565 + WRITE(p, " col = ivec4(t.rgb * vec3(31.99, 63.99, 31.99), 0);\n"); + WRITE(p, " index0 = (col.b << 11) | (col.g << 5) | (col.r);\n"); + WRITE(p, " if (bilinear) {\n"); + WRITE(p, " col = ivec4(t1.rgb * vec3(31.99, 63.99, 31.99), 0);\n"); + WRITE(p, " index1 = (col.b << 11) | (col.g << 5) | (col.r);\n"); + WRITE(p, " col = ivec4(t2.rgb * vec3(31.99, 63.99, 31.99), 0);\n"); + WRITE(p, " index2 = (col.b << 11) | (col.g << 5) | (col.r);\n"); + WRITE(p, " col = ivec4(t3.rgb * vec3(31.99, 63.99, 31.99), 0);\n"); + WRITE(p, " index3 = (col.b << 11) | (col.g << 5) | (col.r);\n"); + WRITE(p, " }\n"); + WRITE(p, " break;\n"); + WRITE(p, " case 1:\n"); // 5551 + WRITE(p, " col = ivec4(t.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n"); + WRITE(p, " index0 = (col.a << 15) | (col.b << 10) | (col.g << 5) | (col.r);\n"); + WRITE(p, " if (bilinear) {\n"); + WRITE(p, " col = ivec4(t1.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n"); + WRITE(p, " index1 = (col.a << 15) | (col.b << 10) | (col.g << 5) | (col.r);\n"); + WRITE(p, " col = ivec4(t2.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n"); + WRITE(p, " index2 = (col.a << 15) | (col.b << 10) | (col.g << 5) | (col.r);\n"); + WRITE(p, " col = ivec4(t3.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n"); + WRITE(p, " index3 = (col.a << 15) | (col.b << 10) | (col.g << 5) | (col.r);\n"); + WRITE(p, " }\n"); + WRITE(p, " break;\n"); + WRITE(p, " case 2:\n"); // 4444 + WRITE(p, " col = ivec4(t.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n"); + WRITE(p, " index0 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n"); + WRITE(p, " if (bilinear) {\n"); + WRITE(p, " col = ivec4(t1.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n"); + WRITE(p, " index1 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n"); + WRITE(p, " col = ivec4(t2.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n"); + WRITE(p, " index2 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n"); + WRITE(p, " col = ivec4(t3.rgba * vec4(15.99, 15.99, 15.99, 15.99));\n"); + WRITE(p, " index3 = (col.a << 12) | (col.b << 8) | (col.g << 4) | (col.r);\n"); + WRITE(p, " }\n"); + WRITE(p, " break;\n"); + WRITE(p, " case 3:\n"); // 8888 + WRITE(p, " col = ivec4(t.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n"); + WRITE(p, " index0 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n"); + WRITE(p, " if (bilinear) {\n"); + WRITE(p, " col = ivec4(t1.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n"); + WRITE(p, " index1 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n"); + WRITE(p, " col = ivec4(t2.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n"); + WRITE(p, " index2 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n"); + WRITE(p, " col = ivec4(t3.rgba * vec4(255.99, 255.99, 255.99, 255.99));\n"); + WRITE(p, " index3 = (col.a << 24) | (col.b << 16) | (col.g << 8) | (col.r);\n"); + WRITE(p, " }\n"); + WRITE(p, " break;\n"); + WRITE(p, " };\n"); + WRITE(p, " index0 = ((index0 >> depalShift) & depalMask) | depalOffset;\n"); + WRITE(p, " t = texelFetch(pal, ivec2(index0, 0), 0);\n"); + WRITE(p, " if (bilinear) {\n"); + WRITE(p, " index1 = ((index1 >> depalShift) & depalMask) | depalOffset;\n"); + WRITE(p, " index2 = ((index2 >> depalShift) & depalMask) | depalOffset;\n"); + WRITE(p, " index3 = ((index3 >> depalShift) & depalMask) | depalOffset;\n"); + WRITE(p, " t1 = texelFetch(pal, ivec2(index1, 0), 0);\n"); + WRITE(p, " t2 = texelFetch(pal, ivec2(index2, 0), 0);\n"); + WRITE(p, " t3 = texelFetch(pal, ivec2(index3, 0), 0);\n"); + WRITE(p, " t = mix(t, t1, fraction.x);\n"); + WRITE(p, " t2 = mix(t2, t3, fraction.x);\n"); + WRITE(p, " t = mix(t, t2, fraction.y);\n"); + WRITE(p, " }\n"); + } + + if (texFunc != GE_TEXFUNC_REPLACE || !doTextureAlpha) + WRITE(p, " vec4 p = v_color0;\n"); if (doTextureAlpha) { // texfmt == RGBA switch (texFunc) { diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 0fee3e6500..63bf981bcc 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -103,6 +103,7 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, queries.push_back({ &u_blendFixA, "u_blendFixA" }); queries.push_back({ &u_blendFixB, "u_blendFixB" }); queries.push_back({ &u_fbotexSize, "u_fbotexSize" }); + queries.push_back({ &u_pal, "pal" }); // Transform queries.push_back({ &u_view, "u_view" }); @@ -161,6 +162,7 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, queries.push_back({ &u_spline_count_v, "u_spline_count_v" }); queries.push_back({ &u_spline_type_u, "u_spline_type_u" }); queries.push_back({ &u_spline_type_v, "u_spline_type_v" }); + queries.push_back({ &u_depal, "u_depal" }); attrMask = vs->GetAttrMask(); availableUniforms = vs->GetUniformMask() | fs->GetUniformMask(); @@ -169,6 +171,7 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, initialize.push_back({ &u_tex, 0, 0 }); initialize.push_back({ &u_fbotex, 0, 1 }); initialize.push_back({ &u_testtex, 0, 2 }); + initialize.push_back({ &u_pal, 0, 3 }); // CLUT initialize.push_back({ &u_tess_pos_tex, 0, 4 }); // Texture unit 4 initialize.push_back({ &u_tess_tex_tex, 0, 5 }); // Texture unit 5 initialize.push_back({ &u_tess_col_tex, 0, 6 }); // Texture unit 6 @@ -283,6 +286,17 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid) { if (!dirty) return; + if (dirty & DIRTY_DEPAL) { + int indexMask = gstate.getClutIndexMask(); + int indexShift = gstate.getClutIndexShift(); + int indexOffset = gstate.getClutIndexStartPos() >> 4; + int format = gstate_c.depalFramebufferFormat; + uint32_t val = BytesToUint32(indexMask, indexShift, indexOffset, format); + // Poke in a bilinear filter flag in the top bit. + val |= gstate.isMagnifyFilteringEnabled() << 31; + render_->SetUniformI1(&u_depal, val); + } + // Update any dirty uniforms before we draw if (dirty & DIRTY_PROJMATRIX) { Matrix4x4 flippedMatrix; @@ -810,7 +824,7 @@ std::string ShaderManagerGLES::DebugGetShaderString(std::string id, DebugShaderT // as sometimes these features might have an effect on the ID bits. #define CACHE_HEADER_MAGIC 0x83277592 -#define CACHE_VERSION 11 +#define CACHE_VERSION 12 struct CacheHeader { uint32_t magic; uint32_t version; diff --git a/GPU/GLES/ShaderManagerGLES.h b/GPU/GLES/ShaderManagerGLES.h index e04a9a8c8b..c9ca56f15a 100644 --- a/GPU/GLES/ShaderManagerGLES.h +++ b/GPU/GLES/ShaderManagerGLES.h @@ -85,6 +85,10 @@ public: int u_blendFixB; int u_fbotexSize; + // Shader depal + int u_pal; // the texture + int u_depal; // the params + // Fragment processing inputs int u_alphacolorref; int u_alphacolormask; diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 38d5aa280a..031c1bdad5 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -161,7 +161,7 @@ void TextureCacheGLES::UpdateSamplingParams(TexCacheEntry &entry, bool force) { render_->SetTextureSampler(0, sClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT, tClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT, MagFiltGL[magFilt], MinFiltGL[minFilt], aniso); } -void TextureCacheGLES::SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight) { +void TextureCacheGLES::SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight, bool forcePoint) { int minFilt; int magFilt; bool sClamp; @@ -171,6 +171,10 @@ void TextureCacheGLES::SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferH GetSamplingParams(minFilt, magFilt, sClamp, tClamp, lodBias, 0, 0, mode); minFilt &= 1; // framebuffers can't mipmap. + if (forcePoint) { + minFilt &= ~1; + magFilt &= ~1; + } // Often the framebuffer will not match the texture size. We'll wrap/clamp in the shader in that case. // This happens whether we have OES_texture_npot or not. @@ -324,6 +328,7 @@ void TextureCacheGLES::BindTexture(TexCacheEntry *entry) { lastBoundTexture = entry->textureName; } UpdateSamplingParams(*entry, false); + gstate_c.useShaderDepal = false; } void TextureCacheGLES::Unbind() { @@ -434,7 +439,33 @@ protected: void TextureCacheGLES::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffer *framebuffer) { DepalShader *depal = nullptr; uint32_t clutMode = gstate.clutformat & 0xFFFFFF; + +#if 0 + bool useShaderDepal = gstate_c.Supports(GPU_SUPPORTS_GLSL_ES_300); +#else + bool useShaderDepal = false; +#endif + if ((entry->status & TexCacheEntry::STATUS_DEPALETTIZE) && !g_Config.bDisableSlowFramebufEffects) { + if (useShaderDepal) { + const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); + GLRTexture *clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_); + render_->BindTexture(TEX_SLOT_CLUT, clutTexture); + framebufferManagerGL_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET); + SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight, true); + InvalidateLastTexture(); + + // Since we started/ended render passes, might need these. + gstate_c.Dirty(DIRTY_DEPAL); + gstate_c.useShaderDepal = true; + gstate_c.depalFramebufferFormat = framebuffer->drawnFormat; + const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16); + const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor; + TexCacheEntry::TexStatus alphaStatus = CheckAlpha((const uint8_t *)clutBuf_, getClutDestFormat(clutFormat), clutTotalColors, clutTotalColors, 1); + gstate_c.SetTextureFullAlpha(alphaStatus == TexCacheEntry::STATUS_ALPHA_FULL); + return; + } + depal = depalShaderCache_->GetDepalettizeShader(clutMode, framebuffer->drawnFormat); } if (depal) { @@ -472,7 +503,7 @@ void TextureCacheGLES::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFram } framebufferManagerGL_->RebindFramebuffer(); - SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight); + SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight, false); InvalidateLastTexture(); diff --git a/GPU/GLES/TextureCacheGLES.h b/GPU/GLES/TextureCacheGLES.h index a88eb8d7ec..be60d7eb77 100644 --- a/GPU/GLES/TextureCacheGLES.h +++ b/GPU/GLES/TextureCacheGLES.h @@ -63,7 +63,7 @@ public: } } - void SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight); + void SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight, bool forcePoint); bool GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) override; void DeviceLost(); diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index ce13132cce..653125d3fe 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -4,6 +4,7 @@ #include "GLRenderManager.h" #include "DataFormatGL.h" #include "base/logging.h" +#include "base/stringutil.h" #include "gfx/gl_common.h" #include "gfx/gl_debug_log.h" #include "gfx_es2/gpu_features.h" @@ -156,9 +157,10 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { #ifdef _WIN32 OutputDebugStringUTF8(buf); - OutputDebugStringUTF8(vsCode); + if (vsCode) + OutputDebugStringUTF8(LineNumberString(vsCode).c_str()); if (fsCode) - OutputDebugStringUTF8(fsCode); + OutputDebugStringUTF8(LineNumberString(fsCode).c_str()); #endif delete[] buf; } else { From f17890623c4fe4c6274c6c97d40e84a5a72a4fb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 13 Apr 2018 20:57:36 +0200 Subject: [PATCH 40/40] Shader depal: fix bilinear filter coord --- GPU/GLES/FragmentShaderGeneratorGLES.cpp | 2 +- GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/GPU/GLES/FragmentShaderGeneratorGLES.cpp b/GPU/GLES/FragmentShaderGeneratorGLES.cpp index d8b117601f..421d8a9f92 100644 --- a/GPU/GLES/FragmentShaderGeneratorGLES.cpp +++ b/GPU/GLES/FragmentShaderGeneratorGLES.cpp @@ -363,7 +363,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, uint64_t *uniform WRITE(p, " int depalOffset = (((u_depal >> 16) & 0xFF) << 4);\n"); WRITE(p, " int depalFmt = ((u_depal >> 24) & 0x3);\n"); WRITE(p, " bool bilinear = (u_depal >> 31) != 0;\n"); - WRITE(p, " vec2 fraction = fract(%s.xy);\n", texcoord); + WRITE(p, " vec2 fraction = fract(%s.xy * vec2(textureSize(tex, 0).xy));\n", texcoord); WRITE(p, " ivec4 col; int index0; int index1; int index2; int index3;\n"); WRITE(p, " switch (depalFmt) {\n"); // We might want to include fmt in the shader ID if this is a performance issue. WRITE(p, " case 0:\n"); // 565 diff --git a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp index 08327ec747..c00c35082b 100644 --- a/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/FragmentShaderGeneratorVulkan.cpp @@ -197,7 +197,7 @@ bool GenerateVulkanGLSLFragmentShader(const FShaderID &id, char *buffer) { WRITE(p, " uint depalOffset = ((base.depal_mask_shift_off_fmt >> 16) & 0xFF) << 4;\n"); WRITE(p, " uint depalFmt = (base.depal_mask_shift_off_fmt >> 24) & 0x3;\n"); WRITE(p, " bool bilinear = (base.depal_mask_shift_off_fmt >> 31) != 0;\n"); - WRITE(p, " vec2 fraction = fract(%s.xy);\n", texcoord); + WRITE(p, " vec2 fraction = fract(%s.xy * vec2(textureSize(tex, 0).xy));\n", texcoord); WRITE(p, " uvec4 col; uint index0; uint index1; uint index2; uint index3;\n"); WRITE(p, " switch (depalFmt) {\n"); // We might want to include fmt in the shader ID if this is a performance issue. WRITE(p, " case 0:\n"); // 565