mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-02-24 07:02:27 +01:00
byuu says: Changelog: - gb/cpu: force STAT mode to 0 when LCD is disabled (fixes Pokemon Pinball, etc) - gb/ppu: when LCD is disabled, require at least one-frame wait to re-enable, display white during this time - todo: should step by a scanline at a time: worst-case is an extra 99% of a frame to enable again - gba/ppu: cache tilemap lookups and attribute parsing - it's more accurate because the GBA wouldn't read this for every pixel - but unfortunately, this didn't provide any speedup at all ... sigh - ruby/audio/alsa: fixed const issue with free() - ruby/video/cgl: removed `glDisable(GL_ALPHA_TEST)` [deprecated] - ruby/video/cgl: removed `glEnable(GL_TEXTURE_2D)` [unnecessary as we use shaders] - processor/lr35902: started rewrite¹ ¹: so, the Game Boy and Game Boy Color cores will be completely broken for at least the next two or three WIPs. The old LR35902 was complete garbage, written in early 2011. So I'm rewriting it to provide a massive cleanup and consistency with other processor cores, especially the Z80 core. I've got about 85% of the main instructions implemented, and then I have to do the CB instructions. The CB instructions are easier because they're mostly just a small number of opcodes in many small variations, but it'll still be tedious.
210 lines
7.2 KiB
C++
210 lines
7.2 KiB
C++
auto OpenGL::shader(const string& pathname) -> void {
|
|
for(auto& program : programs) program.release();
|
|
programs.reset();
|
|
|
|
settings.reset();
|
|
|
|
format = inputFormat;
|
|
filter = GL_LINEAR;
|
|
wrap = GL_CLAMP_TO_BORDER;
|
|
absoluteWidth = 0, absoluteHeight = 0;
|
|
relativeWidth = 0, relativeHeight = 0;
|
|
|
|
uint historySize = 0;
|
|
if(pathname) {
|
|
auto document = BML::unserialize(file::read({pathname, "manifest.bml"}));
|
|
|
|
for(auto node : document["settings"]) {
|
|
settings.insert({node.name(), node.text()});
|
|
}
|
|
|
|
for(auto node : document["input"]) {
|
|
if(node.name() == "history") historySize = node.natural();
|
|
if(node.name() == "format") format = glrFormat(node.text());
|
|
if(node.name() == "filter") filter = glrFilter(node.text());
|
|
if(node.name() == "wrap") wrap = glrWrap(node.text());
|
|
}
|
|
|
|
for(auto node : document["output"]) {
|
|
string text = node.text();
|
|
if(node.name() == "width") {
|
|
if(text.endsWith("%")) relativeWidth = toReal(text.trimRight("%", 1L)) / 100.0;
|
|
else absoluteWidth = text.natural();
|
|
}
|
|
if(node.name() == "height") {
|
|
if(text.endsWith("%")) relativeHeight = toReal(text.trimRight("%", 1L)) / 100.0;
|
|
else absoluteHeight = text.natural();
|
|
}
|
|
}
|
|
|
|
for(auto node : document.find("program")) {
|
|
uint n = programs.size();
|
|
programs(n).bind(this, node, pathname);
|
|
}
|
|
}
|
|
|
|
//changing shaders may change input format, which requires the input texture to be recreated
|
|
if(texture) { glDeleteTextures(1, &texture); texture = 0; }
|
|
glGenTextures(1, &texture);
|
|
glBindTexture(GL_TEXTURE_2D, texture);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, getFormat(), getType(), buffer);
|
|
allocateHistory(historySize);
|
|
}
|
|
|
|
auto OpenGL::allocateHistory(uint size) -> void {
|
|
for(auto& frame : history) glDeleteTextures(1, &frame.texture);
|
|
history.reset();
|
|
while(size--) {
|
|
OpenGLTexture frame;
|
|
frame.filter = filter;
|
|
frame.wrap = wrap;
|
|
glGenTextures(1, &frame.texture);
|
|
glBindTexture(GL_TEXTURE_2D, frame.texture);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, format, frame.width = width, frame.height = height, 0, getFormat(), getType(), buffer);
|
|
history.append(frame);
|
|
}
|
|
}
|
|
|
|
auto OpenGL::clear() -> void {
|
|
for(auto& p : programs) {
|
|
glUseProgram(p.program);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, p.framebuffer);
|
|
glClearColor(0, 0, 0, 1);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
}
|
|
glUseProgram(0);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
glClearColor(0, 0, 0, 1);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
}
|
|
|
|
auto OpenGL::lock(uint32_t*& data, uint& pitch) -> bool {
|
|
pitch = width * sizeof(uint32_t);
|
|
return data = buffer;
|
|
}
|
|
|
|
auto OpenGL::output() -> void {
|
|
clear();
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, texture);
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, getFormat(), getType(), buffer);
|
|
|
|
struct Source {
|
|
GLuint texture;
|
|
uint width, height;
|
|
GLuint filter, wrap;
|
|
};
|
|
vector<Source> sources;
|
|
sources.prepend({texture, width, height, filter, wrap});
|
|
|
|
for(auto& p : programs) {
|
|
uint targetWidth = p.absoluteWidth ? p.absoluteWidth : outputWidth;
|
|
uint targetHeight = p.absoluteHeight ? p.absoluteHeight : outputHeight;
|
|
if(p.relativeWidth) targetWidth = sources[0].width * p.relativeWidth;
|
|
if(p.relativeHeight) targetHeight = sources[0].height * p.relativeHeight;
|
|
|
|
p.size(targetWidth, targetHeight);
|
|
glUseProgram(p.program);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, p.framebuffer);
|
|
|
|
glrUniform1i("phase", p.phase);
|
|
glrUniform1i("historyLength", history.size());
|
|
glrUniform1i("sourceLength", sources.size());
|
|
glrUniform1i("pixmapLength", p.pixmaps.size());
|
|
glrUniform4f("targetSize", targetWidth, targetHeight, 1.0 / targetWidth, 1.0 / targetHeight);
|
|
glrUniform4f("outputSize", outputWidth, outputHeight, 1.0 / outputWidth, 1.0 / outputHeight);
|
|
|
|
uint aid = 0;
|
|
for(auto& frame : history) {
|
|
glrUniform1i({"history[", aid, "]"}, aid);
|
|
glrUniform4f({"historySize[", aid, "]"}, frame.width, frame.height, 1.0 / frame.width, 1.0 / frame.height);
|
|
glActiveTexture(GL_TEXTURE0 + (aid++));
|
|
glBindTexture(GL_TEXTURE_2D, frame.texture);
|
|
glrParameters(frame.filter, frame.wrap);
|
|
}
|
|
|
|
uint bid = 0;
|
|
for(auto& source : sources) {
|
|
glrUniform1i({"source[", bid, "]"}, aid + bid);
|
|
glrUniform4f({"sourceSize[", bid, "]"}, source.width, source.height, 1.0 / source.width, 1.0 / source.height);
|
|
glActiveTexture(GL_TEXTURE0 + aid + (bid++));
|
|
glBindTexture(GL_TEXTURE_2D, source.texture);
|
|
glrParameters(source.filter, source.wrap);
|
|
}
|
|
|
|
uint cid = 0;
|
|
for(auto& pixmap : p.pixmaps) {
|
|
glrUniform1i({"pixmap[", cid, "]"}, aid + bid + cid);
|
|
glrUniform4f({"pixmapSize[", bid, "]"}, pixmap.width, pixmap.height, 1.0 / pixmap.width, 1.0 / pixmap.height);
|
|
glActiveTexture(GL_TEXTURE0 + aid + bid + (cid++));
|
|
glBindTexture(GL_TEXTURE_2D, pixmap.texture);
|
|
glrParameters(pixmap.filter, pixmap.wrap);
|
|
}
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glrParameters(sources[0].filter, sources[0].wrap);
|
|
p.render(sources[0].width, sources[0].height, targetWidth, targetHeight);
|
|
glBindTexture(GL_TEXTURE_2D, p.texture);
|
|
|
|
p.phase = (p.phase + 1) % p.modulo;
|
|
sources.prepend({p.texture, p.width, p.height, p.filter, p.wrap});
|
|
}
|
|
|
|
uint targetWidth = absoluteWidth ? absoluteWidth : outputWidth;
|
|
uint targetHeight = absoluteHeight ? absoluteHeight : outputHeight;
|
|
if(relativeWidth) targetWidth = sources[0].width * relativeWidth;
|
|
if(relativeHeight) targetHeight = sources[0].height * relativeHeight;
|
|
|
|
glUseProgram(program);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
|
|
glrUniform1i("source[0]", 0);
|
|
glrUniform4f("targetSize", targetWidth, targetHeight, 1.0 / targetWidth, 1.0 / targetHeight);
|
|
glrUniform4f("outputSize", outputWidth, outputHeight, 1.0 / outputWidth, 1.0 / outputHeight);
|
|
|
|
glrParameters(sources[0].filter, sources[0].wrap);
|
|
render(sources[0].width, sources[0].height, outputWidth, outputHeight);
|
|
|
|
if(history.size() > 0) {
|
|
OpenGLTexture frame = history.takeRight();
|
|
|
|
glBindTexture(GL_TEXTURE_2D, frame.texture);
|
|
if(width == frame.width && height == frame.height) {
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, getFormat(), getType(), buffer);
|
|
} else {
|
|
glTexImage2D(GL_TEXTURE_2D, 0, format, frame.width = width, frame.height = height, 0, getFormat(), getType(), buffer);
|
|
}
|
|
|
|
history.prepend(frame);
|
|
}
|
|
}
|
|
|
|
auto OpenGL::initialize() -> bool {
|
|
if(!OpenGLBind()) return false;
|
|
|
|
glDisable(GL_BLEND);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDisable(GL_POLYGON_SMOOTH);
|
|
glDisable(GL_STENCIL_TEST);
|
|
glEnable(GL_DITHER);
|
|
|
|
program = glCreateProgram();
|
|
vertex = glrCreateShader(program, GL_VERTEX_SHADER, OpenGLOutputVertexShader);
|
|
//geometry = glrCreateShader(program, GL_GEOMETRY_SHADER, OpenGLGeometryShader);
|
|
fragment = glrCreateShader(program, GL_FRAGMENT_SHADER, OpenGLFragmentShader);
|
|
OpenGLSurface::allocate();
|
|
glrLinkProgram(program);
|
|
|
|
shader("");
|
|
return initialized = true;
|
|
}
|
|
|
|
auto OpenGL::terminate() -> void {
|
|
if(!initialized) return;
|
|
shader(""); //release shader resources (eg frame[] history)
|
|
OpenGLSurface::release();
|
|
if(buffer) { delete[] buffer; buffer = nullptr; }
|
|
initialized = false;
|
|
}
|