bsnes/ruby/video/opengl/main.hpp
Tim Allen c2975e6898 Update to v103r25 release.
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.
2017-08-04 23:05:12 +10:00

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;
}