move texture cache to a template class. vulkan: OSD on android

store texture in the cache map instead of the heap
make related functions member of the class (CollectCleanup, killtex ->
Clear)
refactor common OSD stuff into rend/osd
vulkan support for OSD
This commit is contained in:
Flyinghead 2019-10-21 16:39:16 +02:00
parent 2a89874812
commit 86818389ac
21 changed files with 868 additions and 455 deletions

View File

@ -102,7 +102,7 @@ TA_context* _pvrrc;
void SetREP(TA_context* cntx);
static void rend_create_renderer();
void dump_frame(const char* file, TA_context* ctx, u8* vram, u8* vram_ref = NULL) {
static void dump_frame(const char* file, TA_context* ctx, u8* vram, u8* vram_ref = NULL) {
FILE* fw = fopen(file, "wb");
//append to it
@ -245,7 +245,8 @@ TA_context* read_frame(const char* file, u8* vram_ref = NULL) {
bool dump_frame_switch = false;
bool rend_frame(TA_context* ctx, bool draw_osd) {
static bool rend_frame(TA_context* ctx)
{
if (dump_frame_switch) {
char name[32];
sprintf(name, "dcframe-%d", FrameCount);
@ -267,12 +268,7 @@ bool rend_frame(TA_context* ctx, bool draw_osd) {
re.Set();
#endif
bool do_swp = proc && renderer->Render();
if (do_swp && draw_osd)
renderer->DrawOSD(false);
return do_swp;
return proc && renderer->Render();
}
bool rend_single_frame()
@ -332,7 +328,7 @@ bool rend_single_frame()
_pvrrc = DequeueRender();
}
while (!_pvrrc);
bool do_swp = rend_frame(_pvrrc, true);
bool do_swp = rend_frame(_pvrrc);
swap_pending = settings.rend.DelayFrameSwapping && do_swp && !_pvrrc->rend.isRenderFramebuffer;
#if !defined(TARGET_NO_THREADS)

View File

@ -476,7 +476,7 @@ void x11_window_destroy()
}
cfgSaveBool("x11", "fullscreen", x11_fullscreen);
XDestroyWindow(x11_disp, x11_win);
x11_win = NULL;
x11_win = (Window)0;
}
if (x11_disp)
{

View File

@ -1,5 +1,3 @@
#include <memory>
#include <unordered_map>
#ifndef TARGET_NO_OPENMP
#include <omp.h>
#endif
@ -7,13 +5,13 @@
#include "TexCache.h"
#include "hw/pvr/pvr_regs.h"
#include "hw/pvr/pvr_mem.h"
#include "hw/pvr/Renderer_if.h"
#include "hw/mem/_vmem.h"
#include "hw/mem/vmem32.h"
#include "hw/sh4/modules/mmu.h"
#include "deps/xbrz/xbrz.h"
#include <xxhash.h>
#include "CustomTexture.h"
#include <png.h>
u8* vq_codebook;
u32 palette_index;
@ -733,40 +731,6 @@ void BaseTextureCacheData::CheckCustomTexture()
}
}
std::unordered_map<u64, std::unique_ptr<BaseTextureCacheData>> TexCache;
void CollectCleanup()
{
vector<u64> list;
u32 TargetFrame = max((u32)120,FrameCount) - 120;
for (const auto& pair : TexCache)
{
if (pair.second->dirty && pair.second->dirty < TargetFrame)
list.push_back(pair.first);
if (list.size() > 5)
break;
}
for (u64 id : list)
{
if (TexCache[id]->Delete())
TexCache.erase(id);
}
}
void killtex()
{
for (auto& pair : TexCache)
pair.second->Delete();
TexCache.clear();
KillTex = false;
INFO_LOG(RENDERER, "Texture cache cleared");
}
void ReadFramebuffer(PixelBuffer<u32>& pb, int& width, int& height)
{
width = (FB_R_SIZE.fb_x_size + 1) << 1; // in 16-bit words
@ -944,3 +908,147 @@ void rend_text_invl(vram_block* bl)
libCore_vramlock_Unlock_block_wb(bl);
}
static FILE* pngfile;
void png_cstd_read(png_structp png_ptr, png_bytep data, png_size_t length)
{
if (fread(data, 1, length, pngfile) != length)
png_error(png_ptr, "Truncated read error");
}
u8* loadPNGData(const string& fname, int &width, int &height)
{
const char* filename=fname.c_str();
FILE* file = fopen(filename, "rb");
pngfile=file;
if (!file)
{
EMUERROR("Error opening %s\n", filename);
return NULL;
}
//header for testing if it is a png
png_byte header[8];
//read the header
if (fread(header, 1, 8, file) != 8)
{
fclose(file);
WARN_LOG(RENDERER, "Not a PNG file : %s", filename);
return NULL;
}
//test if png
int is_png = !png_sig_cmp(header, 0, 8);
if (!is_png)
{
fclose(file);
WARN_LOG(RENDERER, "Not a PNG file : %s", filename);
return NULL;
}
//create png struct
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
NULL, NULL);
if (!png_ptr)
{
fclose(file);
WARN_LOG(RENDERER, "Unable to create PNG struct : %s", filename);
return (NULL);
}
//create png info struct
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
{
png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
WARN_LOG(RENDERER, "Unable to create PNG info : %s", filename);
fclose(file);
return (NULL);
}
//create png info struct
png_infop end_info = png_create_info_struct(png_ptr);
if (!end_info)
{
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
WARN_LOG(RENDERER, "Unable to create PNG end info : %s", filename);
fclose(file);
return (NULL);
}
//png error stuff, not sure libpng man suggests this.
if (setjmp(png_jmpbuf(png_ptr)))
{
fclose(file);
WARN_LOG(RENDERER, "Error during setjmp : %s", filename);
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
return (NULL);
}
//init png reading
//png_init_io(png_ptr, fp);
png_set_read_fn(png_ptr, NULL, png_cstd_read);
//let libpng know you already read the first 8 bytes
png_set_sig_bytes(png_ptr, 8);
// read all the info up to the image data
png_read_info(png_ptr, info_ptr);
//variables to pass to get info
int bit_depth, color_type;
png_uint_32 twidth, theight;
// get info about png
png_get_IHDR(png_ptr, info_ptr, &twidth, &theight, &bit_depth, &color_type,
NULL, NULL, NULL);
//update width and height based on png info
width = twidth;
height = theight;
// Update the png info struct.
png_read_update_info(png_ptr, info_ptr);
// Row size in bytes.
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
// Allocate the image_data as a big block, to be given to opengl
png_byte *image_data = new png_byte[rowbytes * height];
if (!image_data)
{
//clean up memory and close stuff
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
WARN_LOG(RENDERER, "Unable to allocate image_data while loading %s", filename);
fclose(file);
return NULL;
}
//row_pointers is for pointing to image_data for reading the png with libpng
png_bytep *row_pointers = new png_bytep[height];
if (!row_pointers)
{
//clean up memory and close stuff
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
delete[] image_data;
WARN_LOG(RENDERER, "Unable to allocate row_pointer while loading %s", filename);
fclose(file);
return NULL;
}
// set the individual row_pointers to point at the correct offsets of image_data
for (int i = 0; i < height; ++i)
row_pointers[height - 1 - i] = image_data + i * rowbytes;
//read the png into image_data through row_pointers
png_read_image(png_ptr, row_pointers);
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
delete[] row_pointers;
fclose(file);
return image_data;
}

View File

@ -6,6 +6,7 @@
#include "hw/pvr/pvr_regs.h"
#undef ID
#include "hw/pvr/ta_structs.h"
#include "hw/pvr/Renderer_if.h"
extern u8* vq_codebook;
extern u32 palette_index;
@ -706,52 +707,85 @@ struct BaseTextureCacheData
virtual ~BaseTextureCacheData() {}
};
extern std::unordered_map<u64, std::unique_ptr<BaseTextureCacheData>> TexCache;
typedef std::unordered_map<u64, std::unique_ptr<BaseTextureCacheData>>::iterator TexCacheIter;
// Only use TexU and TexV from TSP in the cache key
// TexV : 7, TexU : 7
const TSP TSPTextureCacheMask = { { 7, 7 } };
// TexAddr : 0x1FFFFF, Reserved : 0, StrideSel : 0, ScanOrder : 1, PixelFmt : 7, VQ_Comp : 1, MipMapped : 1
const TCW TCWTextureCacheMask = { { 0x1FFFFF, 0, 0, 1, 7, 1, 1 } };
template<typename Func>
BaseTextureCacheData *getTextureCacheData(TSP tsp, TCW tcw, Func factory)
template<typename Texture>
class BaseTextureCache
{
u64 key = tsp.full & TSPTextureCacheMask.full;
if (tcw.PixelFmt == PixelPal4 || tcw.PixelFmt == PixelPal8)
// Paletted textures have a palette selection that must be part of the key
// We also add the palette type to the key to avoid thrashing the cache
// when the palette type is changed. If the palette type is changed back in the future,
// this texture will stil be available.
key |= ((u64)tcw.full << 32) | ((PAL_RAM_CTRL & 3) << 6);
else
key |= (u64)(tcw.full & TCWTextureCacheMask.full) << 32;
TexCacheIter it = TexCache.find(key);
BaseTextureCacheData* texture;
if (it != TexCache.end())
using TexCacheIter = typename std::unordered_map<u64, Texture>::iterator;
public:
Texture *getTextureCacheData(TSP tsp, TCW tcw)
{
texture = it->second.get();
// Needed if the texture is updated
texture->tcw.StrideSel = tcw.StrideSel;
u64 key = tsp.full & TSPTextureCacheMask.full;
if (tcw.PixelFmt == PixelPal4 || tcw.PixelFmt == PixelPal8)
// Paletted textures have a palette selection that must be part of the key
// We also add the palette type to the key to avoid thrashing the cache
// when the palette type is changed. If the palette type is changed back in the future,
// this texture will stil be available.
key |= ((u64)tcw.full << 32) | ((PAL_RAM_CTRL & 3) << 6);
else
key |= (u64)(tcw.full & TCWTextureCacheMask.full) << 32;
TexCacheIter it = cache.find(key);
Texture* texture;
if (it != cache.end())
{
texture = &it->second;
// Needed if the texture is updated
texture->tcw.StrideSel = tcw.StrideSel;
}
else //create if not existing
{
texture = &cache[key];
texture->tsp = tsp;
texture->tcw = tcw;
}
texture->Lookups++;
return texture;
}
else //create if not existing
void CollectCleanup()
{
texture = factory();
TexCache[key] = std::unique_ptr<BaseTextureCacheData>(texture);
vector<u64> list;
texture->tsp = tsp;
texture->tcw = tcw;
u32 TargetFrame = max((u32)120, FrameCount) - 120;
for (const auto& pair : cache)
{
if (pair.second.dirty && pair.second.dirty < TargetFrame)
list.push_back(pair.first);
if (list.size() > 5)
break;
}
for (u64 id : list)
{
if (cache[id].Delete())
cache.erase(id);
}
}
texture->Lookups++;
return texture;
}
void Clear()
{
for (auto& pair : cache)
pair.second.Delete();
cache.clear();
KillTex = false;
INFO_LOG(RENDERER, "Texture cache cleared");
}
private:
std::unordered_map<u64, Texture> cache;
// Only use TexU and TexV from TSP in the cache key
// TexV : 7, TexU : 7
const TSP TSPTextureCacheMask = { { 7, 7 } };
// TexAddr : 0x1FFFFF, Reserved : 0, StrideSel : 0, ScanOrder : 1, PixelFmt : 7, VQ_Comp : 1, MipMapped : 1
const TCW TCWTextureCacheMask = { { 0x1FFFFF, 0, 0, 1, 7, 1, 1 } };
};
void CollectCleanup();
void killtex();
void rend_text_invl(vram_block* bl);
void ReadFramebuffer(PixelBuffer<u32>& pb, int& width, int& height);
@ -766,3 +800,4 @@ static inline void MakeFogTexture(u8 *tex_data)
tex_data[i + 128] = fog_table[i * 4 + 1];
}
}
u8* loadPNGData(const string& fname, int &width, int &height);

View File

@ -1001,9 +1001,7 @@ struct gl4rend : Renderer
glcache.DeleteTextures(1, &depthSaveTexId);
depthSaveTexId = 0;
}
killtex();
CollectCleanup();
TexCache.Clear();
gl_free_osd_resources();
free_output_framebuffer();
@ -1011,16 +1009,20 @@ struct gl4rend : Renderer
}
bool Process(TA_context* ctx) override { return ProcessFrame(ctx); }
bool Render() override { return RenderFrame(); }
bool Render() override
{
RenderFrame();
if (!pvrrc.isRTT)
DrawOSD(false);
return !pvrrc.isRTT;
}
bool RenderLastFrame() override { return !theGLContext.IsSwapBufferPreserved() ? gl4_render_output_framebuffer() : false; }
void Present() override { theGLContext.Swap(); }
void DrawOSD(bool clear_screen) override
{
glBindVertexArray(gl4.vbo.main_vao);
glBindBuffer(GL_ARRAY_BUFFER, gl4.vbo.geometry); glCheck();
OSD_DRAW(clear_screen);
}

View File

@ -5,6 +5,7 @@
#include "rend/gui.h"
#include "wsi/gl_context.h"
#include "cfg/cfg.h"
#include "rend/osd.h"
#ifdef GLES
#ifndef GL_RED
@ -381,13 +382,11 @@ out highp vec4 FragColor; \n\
\n\
in lowp vec4 vtx_base; \n\
in mediump vec2 vtx_uv; \n\
/* Vertex input*/ \n\
\n\
uniform sampler2D tex; \n\
void main() \n\
{ \n\
mediump vec2 uv=vtx_uv; \n\
uv.y=1.0-uv.y; \n\
gl_FragColor = vtx_base*texture(tex,uv.st); \n\
gl_FragColor = vtx_base * texture(tex, vtx_uv); \n\
}";
GLCache glcache;
@ -746,7 +745,34 @@ bool CompilePipelineShader( PipelineShader* s)
return glIsProgram(s->program)==GL_TRUE;
}
GLuint osd_tex;
static void SetupOSDVBO()
{
#ifndef GLES2
if (gl.gl_major >= 3)
{
if (gl.OSD_SHADER.vao == 0)
glGenVertexArrays(1, &gl.OSD_SHADER.vao);
glBindVertexArray(gl.OSD_SHADER.vao);
}
#endif
if (gl.OSD_SHADER.geometry == 0)
glGenBuffers(1, &gl.OSD_SHADER.geometry);
glBindBuffer(GL_ARRAY_BUFFER, gl.OSD_SHADER.geometry);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
//setup vertex buffers attrib pointers
glEnableVertexAttribArray(VERTEX_POS_ARRAY);
glVertexAttribPointer(VERTEX_POS_ARRAY, 2, GL_FLOAT, GL_FALSE, sizeof(OSDVertex), (void*)offsetof(OSDVertex, x));
glEnableVertexAttribArray(VERTEX_COL_BASE_ARRAY);
glVertexAttribPointer(VERTEX_COL_BASE_ARRAY, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(OSDVertex), (void*)offsetof(OSDVertex, r));
glEnableVertexAttribArray(VERTEX_UV_ARRAY);
glVertexAttribPointer(VERTEX_UV_ARRAY, 2, GL_FLOAT, GL_FALSE, sizeof(OSDVertex), (void*)offsetof(OSDVertex, u));
glDisableVertexAttribArray(VERTEX_COL_OFFS_ARRAY);
glCheck();
}
void gl_load_osd_resources()
{
@ -762,19 +788,26 @@ void gl_load_osd_resources()
#ifdef __ANDROID__
int w, h;
if (osd_tex == 0)
osd_tex = loadPNG(get_readonly_data_path(DATA_PATH "buttons.png"), w, h);
if (gl.OSD_SHADER.osd_tex == 0)
gl.OSD_SHADER.osd_tex = loadPNG(get_readonly_data_path(DATA_PATH "buttons.png"), w, h);
#endif
SetupOSDVBO();
}
void gl_free_osd_resources()
{
glcache.DeleteProgram(gl.OSD_SHADER.program);
if (osd_tex != 0) {
glcache.DeleteTextures(1, &osd_tex);
osd_tex = 0;
if (gl.OSD_SHADER.osd_tex != 0) {
glcache.DeleteTextures(1, &gl.OSD_SHADER.osd_tex);
gl.OSD_SHADER.osd_tex = 0;
}
glDeleteBuffers(1, &gl.OSD_SHADER.geometry);
gl.OSD_SHADER.geometry = 0;
#ifndef GLES2
glDeleteVertexArrays(1, &gl.OSD_SHADER.vao);
gl.OSD_SHADER.vao = 0;
#endif
}
static void create_modvol_shader()
@ -893,143 +926,23 @@ void UpdateFogTexture(u8 *fog_table, GLenum texture_slot, GLint fog_image_format
glActiveTexture(GL_TEXTURE0);
}
extern u16 kcode[4];
extern u8 rt[4],lt[4];
#define VJOY_VISIBLE 14
#if defined(__ANDROID__)
extern float vjoy_pos[15][8];
#else
float vjoy_pos[15][8]=
{
{24+0,24+64,64,64}, //LEFT
{24+64,24+0,64,64}, //UP
{24+128,24+64,64,64}, //RIGHT
{24+64,24+128,64,64}, //DOWN
{440+0,280+64,64,64}, //X
{440+64,280+0,64,64}, //Y
{440+128,280+64,64,64}, //B
{440+64,280+128,64,64}, //A
{320-32,360+32,64,64}, //Start
{440,200,90,64}, //LT
{542,200,90,64}, //RT
{-24,128+224,128,128}, //ANALOG_RING
{96,320,64,64}, //ANALOG_POINT
{320-32,24,64,64}, // FFORWARD
{1} // VJOY_VISIBLE
};
#endif // !__ANDROID__
static List<Vertex> osd_vertices;
static bool osd_vertices_overrun;
static const float vjoy_sz[2][15] = {
{ 64,64,64,64, 64,64,64,64, 64, 90,90, 128, 64, 64 },
{ 64,64,64,64, 64,64,64,64, 64, 64,64, 128, 64, 64 },
};
void HideOSD()
{
vjoy_pos[VJOY_VISIBLE][0] = 0;
}
static void DrawButton(float* xy, u32 state)
{
Vertex vtx;
vtx.z = 1;
vtx.col[0]=vtx.col[1]=vtx.col[2]=(0x7F-0x40*state/255)*vjoy_pos[VJOY_VISIBLE][0];
vtx.col[3]=0xA0*vjoy_pos[VJOY_VISIBLE][4];
vjoy_pos[VJOY_VISIBLE][4]+=(vjoy_pos[VJOY_VISIBLE][0]-vjoy_pos[VJOY_VISIBLE][4])/2;
vtx.x = xy[0]; vtx.y = xy[1];
vtx.u=xy[4]; vtx.v=xy[5];
*osd_vertices.Append() = vtx;
vtx.x = xy[0] + xy[2]; vtx.y = xy[1];
vtx.u=xy[6]; vtx.v=xy[5];
*osd_vertices.Append() = vtx;
vtx.x = xy[0]; vtx.y = xy[1] + xy[3];
vtx.u=xy[4]; vtx.v=xy[7];
*osd_vertices.Append() = vtx;
vtx.x = xy[0] + xy[2]; vtx.y = xy[1] + xy[3];
vtx.u=xy[6]; vtx.v=xy[7];
*osd_vertices.Append() = vtx;
}
static void DrawButton2(float* xy, bool state) { DrawButton(xy,state?0:255); }
static void osd_gen_vertices()
{
osd_vertices.Init(ARRAY_SIZE(vjoy_pos) * 4, &osd_vertices_overrun, "OSD vertices");
DrawButton2(vjoy_pos[0],kcode[0] & DC_DPAD_LEFT);
DrawButton2(vjoy_pos[1],kcode[0] & DC_DPAD_UP);
DrawButton2(vjoy_pos[2],kcode[0] & DC_DPAD_RIGHT);
DrawButton2(vjoy_pos[3],kcode[0] & DC_DPAD_DOWN);
DrawButton2(vjoy_pos[4],kcode[0] & DC_BTN_X);
DrawButton2(vjoy_pos[5],kcode[0] & DC_BTN_Y);
DrawButton2(vjoy_pos[6],kcode[0] & DC_BTN_B);
DrawButton2(vjoy_pos[7],kcode[0] & DC_BTN_A);
DrawButton2(vjoy_pos[8],kcode[0] & DC_BTN_START);
DrawButton(vjoy_pos[9],lt[0]);
DrawButton(vjoy_pos[10],rt[0]);
DrawButton2(vjoy_pos[11],1);
DrawButton2(vjoy_pos[12],0);
DrawButton2(vjoy_pos[13], 0);
}
#define OSD_TEX_W 512
#define OSD_TEX_H 256
void OSD_DRAW(bool clear_screen)
{
#ifdef __ANDROID__
if (osd_tex == 0)
if (gl.OSD_SHADER.osd_tex == 0)
gl_load_osd_resources();
if (osd_tex != 0)
if (gl.OSD_SHADER.osd_tex != 0)
{
osd_gen_vertices();
const std::vector<OSDVertex>& osdVertices = GetOSDVertices();
float u=0;
float v=0;
#ifndef GLES2
if (gl.gl_major >= 3)
glBindVertexArray(gl.OSD_SHADER.vao);
else
#endif
SetupOSDVBO();
for (int i = 0; i < 14; i++)
{
//umin,vmin,umax,vmax
vjoy_pos[i][4]=(u+1)/OSD_TEX_W;
vjoy_pos[i][5]=(v+1)/OSD_TEX_H;
vjoy_pos[i][6]=((u+vjoy_sz[0][i]-1))/OSD_TEX_W;
vjoy_pos[i][7]=((v+vjoy_sz[1][i]-1))/OSD_TEX_H;
u+=vjoy_sz[0][i];
if (u>=OSD_TEX_W)
{
u-=OSD_TEX_W;
v+=vjoy_sz[1][i];
}
//v+=vjoy_pos[i][3];
}
glBindBuffer(GL_ARRAY_BUFFER, gl.OSD_SHADER.geometry);
verify(glIsProgram(gl.OSD_SHADER.program));
glcache.UseProgram(gl.OSD_SHADER.program);
@ -1044,11 +957,11 @@ void OSD_DRAW(bool clear_screen)
glUniform4fv(gl.OSD_SHADER.scale, 1, scale);
glActiveTexture(GL_TEXTURE0);
glcache.BindTexture(GL_TEXTURE_2D, osd_tex);
glcache.BindTexture(GL_TEXTURE_2D, gl.OSD_SHADER.osd_tex);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBufferData(GL_ARRAY_BUFFER, osd_vertices.bytes(), osd_vertices.head(), GL_STREAM_DRAW); glCheck();
glBufferData(GL_ARRAY_BUFFER, osdVertices.size() * sizeof(OSDVertex), osdVertices.data(), GL_STREAM_DRAW); glCheck();
glcache.Enable(GL_BLEND);
glcache.Disable(GL_DEPTH_TEST);
@ -1066,10 +979,12 @@ void OSD_DRAW(bool clear_screen)
glcache.ClearColor(0.7f, 0.7f, 0.7f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
}
int dfa = osd_vertices.used() / 4;
int dfa = osdVertices.size() / 4;
for (int i = 0; i < dfa; i++)
glDrawArrays(GL_TRIANGLE_STRIP, i * 4, 4);
glCheck();
}
#endif
gui_display_osd();
@ -1080,7 +995,7 @@ bool ProcessFrame(TA_context* ctx)
ctx->rend_inuse.Lock();
if (KillTex)
killtex();
TexCache.Clear();
if (ctx->rend.isRenderFramebuffer)
{
@ -1092,7 +1007,7 @@ bool ProcessFrame(TA_context* ctx)
if (!ta_parse_vdrc(ctx))
return false;
}
CollectCleanup();
TexCache.CollectCleanup();
if (ctx->rend.Overrun)
WARN_LOG(PVR, "ERROR: TA context overrun");
@ -1575,33 +1490,25 @@ struct glesrend : Renderer
void Resize(int w, int h) override { screen_width=w; screen_height=h; }
void Term() override
{
killtex();
TexCache.Clear();
gles_term();
}
bool Process(TA_context* ctx) override { return ProcessFrame(ctx); }
bool Render() override { return RenderFrame(); }
bool Render() override
{
RenderFrame();
if (!pvrrc.isRTT)
DrawOSD(false);
return !pvrrc.isRTT;
}
bool RenderLastFrame() override { return !theGLContext.IsSwapBufferPreserved() ? render_output_framebuffer() : false; }
void Present() override { theGLContext.Swap(); glViewport(0, 0, screen_width, screen_height); }
void DrawOSD(bool clear_screen) override
{
#ifndef GLES2
if (gl.gl_major >= 3)
glBindVertexArray(gl.vbo.vao);
#endif
glBindBuffer(GL_ARRAY_BUFFER, gl.vbo.geometry);
glEnableVertexAttribArray(VERTEX_POS_ARRAY);
glVertexAttribPointer(VERTEX_POS_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,x));
glEnableVertexAttribArray(VERTEX_COL_BASE_ARRAY);
glVertexAttribPointer(VERTEX_COL_BASE_ARRAY, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex,col));
glEnableVertexAttribArray(VERTEX_UV_ARRAY);
glVertexAttribPointer(VERTEX_UV_ARRAY, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,u));
OSD_DRAW(clear_screen);
glCheck();
}
virtual u64 GetTexture(TSP tsp, TCW tcw) override
@ -1611,150 +1518,6 @@ struct glesrend : Renderer
};
FILE* pngfile;
void png_cstd_read(png_structp png_ptr, png_bytep data, png_size_t length)
{
if (fread(data, 1, length, pngfile) != length)
png_error(png_ptr, "Truncated read error");
}
u8* loadPNGData(const string& fname, int &width, int &height)
{
const char* filename=fname.c_str();
FILE* file = fopen(filename, "rb");
pngfile=file;
if (!file)
{
EMUERROR("Error opening %s\n", filename);
return NULL;
}
//header for testing if it is a png
png_byte header[8];
//read the header
if (fread(header, 1, 8, file) != 8)
{
fclose(file);
WARN_LOG(RENDERER, "Not a PNG file : %s", filename);
return NULL;
}
//test if png
int is_png = !png_sig_cmp(header, 0, 8);
if (!is_png)
{
fclose(file);
WARN_LOG(RENDERER, "Not a PNG file : %s", filename);
return NULL;
}
//create png struct
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
NULL, NULL);
if (!png_ptr)
{
fclose(file);
WARN_LOG(RENDERER, "Unable to create PNG struct : %s", filename);
return (NULL);
}
//create png info struct
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
{
png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
WARN_LOG(RENDERER, "Unable to create PNG info : %s", filename);
fclose(file);
return (NULL);
}
//create png info struct
png_infop end_info = png_create_info_struct(png_ptr);
if (!end_info)
{
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
WARN_LOG(RENDERER, "Unable to create PNG end info : %s", filename);
fclose(file);
return (NULL);
}
//png error stuff, not sure libpng man suggests this.
if (setjmp(png_jmpbuf(png_ptr)))
{
fclose(file);
WARN_LOG(RENDERER, "Error during setjmp : %s", filename);
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
return (NULL);
}
//init png reading
//png_init_io(png_ptr, fp);
png_set_read_fn(png_ptr, NULL, png_cstd_read);
//let libpng know you already read the first 8 bytes
png_set_sig_bytes(png_ptr, 8);
// read all the info up to the image data
png_read_info(png_ptr, info_ptr);
//variables to pass to get info
int bit_depth, color_type;
png_uint_32 twidth, theight;
// get info about png
png_get_IHDR(png_ptr, info_ptr, &twidth, &theight, &bit_depth, &color_type,
NULL, NULL, NULL);
//update width and height based on png info
width = twidth;
height = theight;
// Update the png info struct.
png_read_update_info(png_ptr, info_ptr);
// Row size in bytes.
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
// Allocate the image_data as a big block, to be given to opengl
png_byte *image_data = new png_byte[rowbytes * height];
if (!image_data)
{
//clean up memory and close stuff
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
WARN_LOG(RENDERER, "Unable to allocate image_data while loading %s", filename);
fclose(file);
return NULL;
}
//row_pointers is for pointing to image_data for reading the png with libpng
png_bytep *row_pointers = new png_bytep[height];
if (!row_pointers)
{
//clean up memory and close stuff
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
delete[] image_data;
WARN_LOG(RENDERER, "Unable to allocate row_pointer while loading %s", filename);
fclose(file);
return NULL;
}
// set the individual row_pointers to point at the correct offsets of image_data
for (int i = 0; i < height; ++i)
row_pointers[height - 1 - i] = image_data + i * rowbytes;
//read the png into image_data through row_pointers
png_read_image(png_ptr, row_pointers);
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
delete[] row_pointers;
fclose(file);
return image_data;
}
GLuint loadPNG(const string& fname, int &width, int &height)
{
png_byte *image_data = loadPNGData(fname, width, height);

View File

@ -63,6 +63,9 @@ struct gl_ctx
{
GLuint program;
GLuint scale;
GLuint vao;
GLuint geometry;
GLuint osd_tex;
} OSD_SHADER;
struct
@ -200,4 +203,9 @@ struct TextureCacheData : BaseTextureCacheData
virtual bool Delete() override;
};
class TextureCache : public BaseTextureCache<TextureCacheData>
{
};
extern TextureCache TexCache;
extern const u32 Zfunction[8];

View File

@ -30,6 +30,7 @@ Compression
#endif
extern u32 decoded_colors[3][65536];
TextureCache TexCache;
static void dumpRtTexture(u32 name, u32 w, u32 h) {
char sname[256];
@ -319,7 +320,7 @@ void ReadRTTBuffer() {
for (tsp.TexU = 0; tsp.TexU <= 7 && (8 << tsp.TexU) < w; tsp.TexU++);
for (tsp.TexV = 0; tsp.TexV <= 7 && (8 << tsp.TexV) < h; tsp.TexV++);
TextureCacheData *texture_data = static_cast<TextureCacheData*>(getTextureCacheData(tsp, tcw, [](){ return (BaseTextureCacheData *)new TextureCacheData(); }));
TextureCacheData *texture_data = TexCache.getTextureCacheData(tsp, tcw);
if (texture_data->texID != 0)
glcache.DeleteTextures(1, &texture_data->texID);
else
@ -345,7 +346,7 @@ u64 gl_GetTexture(TSP tsp, TCW tcw)
TexCacheLookups++;
//lookup texture
TextureCacheData* tf = static_cast<TextureCacheData*>(getTextureCacheData(tsp, tcw, [](){ return (BaseTextureCacheData *)new TextureCacheData(); }));
TextureCacheData* tf = TexCache.getTextureCacheData(tsp, tcw);
if (tf->texID == 0)
{
@ -380,7 +381,7 @@ text_info raw_GetTexture(TSP tsp, TCW tcw)
text_info rv = { 0 };
//lookup texture
TextureCacheData* tf = static_cast<TextureCacheData*>(getTextureCacheData(tsp, tcw, [](){ return (BaseTextureCacheData *)new TextureCacheData(); }));
TextureCacheData* tf = TexCache.getTextureCacheData(tsp, tcw);
if (tf->pData == nullptr)
tf->Create();

132
core/rend/osd.cpp Normal file
View File

@ -0,0 +1,132 @@
// Personal message to whoever wrote this code originally: please quit programming. Stop making other people's life miserable
#include "osd.h"
#include "types.h"
#include "input/gamepad.h"
extern u16 kcode[4];
extern u8 rt[4], lt[4];
#if defined(__ANDROID__)
extern float vjoy_pos[15][8];
#else
static float vjoy_pos[15][8]=
{
{24+0,24+64,64,64}, //LEFT
{24+64,24+0,64,64}, //UP
{24+128,24+64,64,64}, //RIGHT
{24+64,24+128,64,64}, //DOWN
{440+0,280+64,64,64}, //X
{440+64,280+0,64,64}, //Y
{440+128,280+64,64,64}, //B
{440+64,280+128,64,64}, //A
{320-32,360+32,64,64}, //Start
{440,200,90,64}, //LT
{542,200,90,64}, //RT
{-24,128+224,128,128}, //ANALOG_RING
{96,320,64,64}, //ANALOG_POINT
{320-32,24,64,64}, // FFORWARD
{1} // VJOY_VISIBLE
};
#endif // !__ANDROID__
static const float vjoy_sz[2][15] = {
{ 64,64,64,64, 64,64,64,64, 64, 90,90, 128, 64, 64 },
{ 64,64,64,64, 64,64,64,64, 64, 64,64, 128, 64, 64 },
};
static std::vector<OSDVertex> osdVertices;
void HideOSD()
{
vjoy_pos[VJOY_VISIBLE][0] = 0;
}
static void DrawButton(const float xy[8], u32 state)
{
OSDVertex vtx;
vtx.r = vtx.g = vtx.b = (0x7F - 0x40 * state / 255) * vjoy_pos[VJOY_VISIBLE][0];
vtx.a = 0xA0 * vjoy_pos[VJOY_VISIBLE][4];
vjoy_pos[VJOY_VISIBLE][4] += (vjoy_pos[VJOY_VISIBLE][0] - vjoy_pos[VJOY_VISIBLE][4]) / 2;
vtx.x = xy[0]; vtx.y = xy[1];
vtx.u = xy[4]; vtx.v = xy[5];
osdVertices.push_back(vtx);
vtx.x = xy[0] + xy[2]; vtx.y = xy[1];
vtx.u = xy[6]; vtx.v = xy[5];
osdVertices.push_back(vtx);
vtx.x = xy[0]; vtx.y = xy[1] + xy[3];
vtx.u = xy[4]; vtx.v = xy[7];
osdVertices.push_back(vtx);
vtx.x = xy[0] + xy[2]; vtx.y = xy[1] + xy[3];
vtx.u = xy[6]; vtx.v = xy[7];
osdVertices.push_back(vtx);
}
static void DrawButton2(const float xy[8], bool state)
{
DrawButton(xy, state ? 0 : 255);
}
const std::vector<OSDVertex>& GetOSDVertices()
{
osdVertices.reserve(ARRAY_SIZE(vjoy_pos) * 4);
osdVertices.clear();
DrawButton2(vjoy_pos[0], kcode[0] & DC_DPAD_LEFT);
DrawButton2(vjoy_pos[1], kcode[0] & DC_DPAD_UP);
DrawButton2(vjoy_pos[2], kcode[0] & DC_DPAD_RIGHT);
DrawButton2(vjoy_pos[3], kcode[0] & DC_DPAD_DOWN);
DrawButton2(vjoy_pos[4], kcode[0] & DC_BTN_X);
DrawButton2(vjoy_pos[5], kcode[0] & DC_BTN_Y);
DrawButton2(vjoy_pos[6], kcode[0] & DC_BTN_B);
DrawButton2(vjoy_pos[7], kcode[0] & DC_BTN_A);
DrawButton2(vjoy_pos[8], kcode[0] & DC_BTN_START);
DrawButton(vjoy_pos[9], lt[0]);
DrawButton(vjoy_pos[10], rt[0]);
DrawButton2(vjoy_pos[11], 1);
DrawButton2(vjoy_pos[12], 0);
DrawButton2(vjoy_pos[13], 0);
return osdVertices;
}
static void setVjoyUV()
{
float u = 0;
float v = 0;
for (int i = 0; i < VJOY_VISIBLE; i++)
{
//umin, vmin, umax, vmax
vjoy_pos[i][4] = (u + 1) / OSD_TEX_W;
vjoy_pos[i][5] = 1.f - (v + 1) / OSD_TEX_H;
vjoy_pos[i][6] = (u + vjoy_sz[0][i] - 1) / OSD_TEX_W;
vjoy_pos[i][7] = 1.f - (v + vjoy_sz[1][i] - 1) / OSD_TEX_H;
u += vjoy_sz[0][i];
if (u >= OSD_TEX_W)
{
u -= OSD_TEX_W;
v += vjoy_sz[1][i];
}
}
}
static OnLoad setVjoyUVOnLoad(&setVjoyUV);

15
core/rend/osd.h Normal file
View File

@ -0,0 +1,15 @@
#include <vector>
#include "types.h"
#define VJOY_VISIBLE 14
#define OSD_TEX_W 512
#define OSD_TEX_H 256
struct OSDVertex
{
float x, y;
float u, v;
u8 r, g, b, a;
};
const std::vector<OSDVertex>& GetOSDVertices();

View File

@ -36,7 +36,7 @@ struct BufferData
void upload(vk::Device const& device, u32 size, const void *data, u32 bufOffset = 0) const
{
verify((m_propertyFlags & vk::MemoryPropertyFlagBits::eHostCoherent) && (m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible));
verify(offset + bufOffset + size <= bufferSize);
verify(bufOffset + size <= bufferSize);
void* dataPtr = device.mapMemory(sharedDeviceMemory, offset + bufOffset, size);
memcpy(dataPtr, data, size);
@ -50,7 +50,7 @@ struct BufferData
u32 totalSize = 0;
for (int i = 0; i < count; i++)
totalSize += sizes[i];
verify(offset + bufOffset + totalSize <= bufferSize);
verify(bufOffset + totalSize <= bufferSize);
void* dataPtr = device.mapMemory(sharedDeviceMemory, offset + bufOffset, totalSize);
for (int i = 0; i < count; i++)
{
@ -64,7 +64,7 @@ struct BufferData
void download(vk::Device const& device, u32 size, void *data, u32 bufOffset = 0) const
{
verify((m_propertyFlags & vk::MemoryPropertyFlagBits::eHostCoherent) && (m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible));
verify(offset + bufOffset + size <= bufferSize);
verify(bufOffset + size <= bufferSize);
void* dataPtr = device.mapMemory(sharedDeviceMemory, offset + bufOffset, size);
memcpy(data, dataPtr, size);

View File

@ -448,8 +448,6 @@ bool Drawer::Draw(const Texture *fogTexture)
if (!is_rtt)
gui_display_osd();
EndRenderPass();
return !is_rtt;
}
@ -477,7 +475,7 @@ vk::CommandBuffer TextureDrawer::BeginRenderPass()
heightPow2 *= settings.rend.RenderToTextureUpscale;
}
static_cast<RttPipelineManager*>(pipelineManager.get())->CheckSettingsChange();
static_cast<RttPipelineManager*>(pipelineManager)->CheckSettingsChange();
VulkanContext *context = GetContext();
vk::Device device = *context->GetDevice();
@ -514,11 +512,14 @@ vk::CommandBuffer TextureDrawer::BeginRenderPass()
for (tsp.TexU = 0; tsp.TexU <= 7 && (8 << tsp.TexU) < origWidth; tsp.TexU++);
for (tsp.TexV = 0; tsp.TexV <= 7 && (8 << tsp.TexV) < origHeight; tsp.TexV++);
texture = static_cast<Texture*>(getTextureCacheData(tsp, tcw, [this](){
return (BaseTextureCacheData *)new Texture(VulkanContext::Instance()->GetPhysicalDevice(), *VulkanContext::Instance()->GetDevice(), this->texAllocator);
}));
texture = textureCache->getTextureCacheData(tsp, tcw);
if (texture->IsNew())
{
texture->Create();
texture->SetAllocator(texAllocator);
texture->SetPhysicalDevice(GetContext()->GetPhysicalDevice());
texture->SetDevice(*GetContext()->GetDevice());
}
if (texture->format != vk::Format::eR8G8B8A8Unorm)
{
texture->extent = vk::Extent2D(widthPow2, heightPow2);

View File

@ -56,17 +56,17 @@ public:
}
protected:
void Init(SamplerManager *samplerManager, ShaderManager *shaderManager)
void Init(SamplerManager *samplerManager, PipelineManager *pipelineManager)
{
this->pipelineManager = pipelineManager;
this->samplerManager = samplerManager;
pipelineManager->Init(shaderManager);
}
virtual DescriptorSets& GetCurrentDescSet() = 0;
virtual BufferData *GetMainBuffer(u32 size) = 0;
VulkanContext *GetContext() const { return VulkanContext::Instance(); }
std::unique_ptr<PipelineManager> pipelineManager;
PipelineManager *pipelineManager = nullptr;
vk::Rect2D baseScissor;
// temp stuff
float scale_x = 1.f;
@ -106,9 +106,10 @@ class ScreenDrawer : public Drawer
public:
void Init(SamplerManager *samplerManager, ShaderManager *shaderManager)
{
if (!pipelineManager)
pipelineManager = std::unique_ptr<PipelineManager>(new PipelineManager());
Drawer::Init(samplerManager, shaderManager);
if (!screenPipelineManager)
screenPipelineManager = std::unique_ptr<PipelineManager>(new PipelineManager());
screenPipelineManager->Init(shaderManager);
Drawer::Init(samplerManager, screenPipelineManager.get());
if (descriptorSets.size() > GetContext()->GetSwapChainSize())
descriptorSets.resize(GetContext()->GetSwapChainSize());
@ -116,7 +117,7 @@ public:
while (descriptorSets.size() < GetContext()->GetSwapChainSize())
{
descriptorSets.push_back(DescriptorSets());
descriptorSets.back().Init(samplerManager, pipelineManager->GetPipelineLayout(), pipelineManager->GetPerFrameDSLayout(), pipelineManager->GetPerPolyDSLayout());
descriptorSets.back().Init(samplerManager, screenPipelineManager->GetPipelineLayout(), screenPipelineManager->GetPerFrameDSLayout(), screenPipelineManager->GetPerPolyDSLayout());
}
}
ScreenDrawer() = default;
@ -158,19 +159,20 @@ private:
std::vector<DescriptorSets> descriptorSets;
std::vector<std::unique_ptr<BufferData>> mainBuffers;
std::unique_ptr<PipelineManager> screenPipelineManager;
};
class TextureDrawer : public Drawer
{
public:
void Init(SamplerManager *samplerManager, ShaderManager *shaderManager, VulkanAllocator *texAllocator)
void Init(SamplerManager *samplerManager, VulkanAllocator *texAllocator, RttPipelineManager *pipelineManager, TextureCache *textureCache)
{
pipelineManager = std::unique_ptr<RttPipelineManager>(new RttPipelineManager());
Drawer::Init(samplerManager, shaderManager);
Drawer::Init(samplerManager, pipelineManager);
descriptorSets.Init(samplerManager, pipelineManager->GetPipelineLayout(), pipelineManager->GetPerFrameDSLayout(), pipelineManager->GetPerPolyDSLayout());
fence = GetContext()->GetDevice()->createFenceUnique(vk::FenceCreateInfo());
this->texAllocator = texAllocator;
this->textureCache = textureCache;
}
void SetCommandPool(CommandPool *commandPool) { this->commandPool = commandPool; }
@ -179,10 +181,10 @@ public:
TextureDrawer(TextureDrawer&& other) = default;
TextureDrawer& operator=(const TextureDrawer& other) = delete;
TextureDrawer& operator=(TextureDrawer&& other) = default;
virtual void EndRenderPass() override;
protected:
virtual vk::CommandBuffer BeginRenderPass() override;
virtual void EndRenderPass() override;
DescriptorSets& GetCurrentDescSet() override { return descriptorSets; }
virtual BufferData* GetMainBuffer(u32 size) override
@ -216,4 +218,5 @@ private:
std::unique_ptr<BufferData> mainBuffer;
CommandPool *commandPool = nullptr;
VulkanAllocator *texAllocator = nullptr;
TextureCache *textureCache = nullptr;
};

View File

@ -20,6 +20,7 @@
*/
#include "pipeline.h"
#include "hw/pvr/Renderer_if.h"
#include "rend/osd.h"
static const vk::CompareOp depthOps[] =
{
@ -379,9 +380,17 @@ void QuadPipeline::CreatePipeline()
vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo;
// Color flags and blending
vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState;
pipelineColorBlendAttachmentState.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG
| vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState(
true, // blendEnable
vk::BlendFactor::eConstantAlpha, // srcColorBlendFactor
vk::BlendFactor::eOneMinusConstantAlpha, // dstColorBlendFactor
vk::BlendOp::eAdd, // colorBlendOp
vk::BlendFactor::eConstantAlpha, // srcAlphaBlendFactor
vk::BlendFactor::eOneMinusConstantAlpha, // dstAlphaBlendFactor
vk::BlendOp::eAdd, // alphaBlendOp
vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG
| vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA
);
vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo
(
vk::PipelineColorBlendStateCreateFlags(), // flags
@ -392,8 +401,9 @@ void QuadPipeline::CreatePipeline()
{ { 1.0f, 1.0f, 1.0f, 1.0f } } // blendConstants
);
vk::DynamicState dynamicStates[2] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };
vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), 2, dynamicStates);
vk::DynamicState dynamicStates[] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor, vk::DynamicState::eBlendConstants };
vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), ARRAY_SIZE(dynamicStates),
dynamicStates);
vk::PipelineShaderStageCreateInfo stages[] = {
{ vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, shaderManager->GetQuadVertexShader(), "main" },
@ -419,3 +429,88 @@ void QuadPipeline::CreatePipeline()
pipeline = GetContext()->GetDevice()->createGraphicsPipelineUnique(GetContext()->GetPipelineCache(), graphicsPipelineCreateInfo);
}
void OSDPipeline::CreatePipeline()
{
// Vertex input state
static const vk::VertexInputBindingDescription vertexBindingDescriptions[] =
{
{ 0, sizeof(OSDVertex) },
};
static const vk::VertexInputAttributeDescription vertexInputAttributeDescriptions[] =
{
vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32Sfloat, offsetof(OSDVertex, x)), // pos
vk::VertexInputAttributeDescription(1, 0, vk::Format::eR8G8B8A8Uint, offsetof(OSDVertex, r)), // color
vk::VertexInputAttributeDescription(2, 0, vk::Format::eR32G32Sfloat, offsetof(OSDVertex, u)), // tex coord
};
vk::PipelineVertexInputStateCreateInfo vertexInputStateCreateInfo(
vk::PipelineVertexInputStateCreateFlags(),
ARRAY_SIZE(vertexBindingDescriptions),
vertexBindingDescriptions,
ARRAY_SIZE(vertexInputAttributeDescriptions),
vertexInputAttributeDescriptions);
// Input assembly state
vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(), vk::PrimitiveTopology::eTriangleStrip);
// Viewport and scissor states
vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(vk::PipelineViewportStateCreateFlags(), 1, nullptr, 1, nullptr);
// Rasterization and multisample states
vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo;
pipelineRasterizationStateCreateInfo.lineWidth = 1.0;
vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo;
// Depth and stencil
vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo;
// Color flags and blending
vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState(
true, // blendEnable
vk::BlendFactor::eSrcAlpha, // srcColorBlendFactor
vk::BlendFactor::eOneMinusSrcAlpha, // dstColorBlendFactor
vk::BlendOp::eAdd, // colorBlendOp
vk::BlendFactor::eSrcAlpha, // srcAlphaBlendFactor
vk::BlendFactor::eOneMinusSrcAlpha, // dstAlphaBlendFactor
vk::BlendOp::eAdd, // alphaBlendOp
vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG
| vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA
);
vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo
(
vk::PipelineColorBlendStateCreateFlags(), // flags
false, // logicOpEnable
vk::LogicOp::eNoOp, // logicOp
1, // attachmentCount
&pipelineColorBlendAttachmentState, // pAttachments
{ { 1.0f, 1.0f, 1.0f, 1.0f } } // blendConstants
);
vk::DynamicState dynamicStates[] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };
vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), ARRAY_SIZE(dynamicStates),
dynamicStates);
vk::PipelineShaderStageCreateInfo stages[] = {
{ vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, shaderManager->GetOSDVertexShader(), "main" },
{ vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, shaderManager->GetOSDFragmentShader(), "main" },
};
vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo
(
vk::PipelineCreateFlags(), // flags
2, // stageCount
stages, // pStages
&vertexInputStateCreateInfo, // pVertexInputState
&pipelineInputAssemblyStateCreateInfo, // pInputAssemblyState
nullptr, // pTessellationState
&pipelineViewportStateCreateInfo, // pViewportState
&pipelineRasterizationStateCreateInfo, // pRasterizationState
&pipelineMultisampleStateCreateInfo, // pMultisampleState
&pipelineDepthStencilStateCreateInfo, // pDepthStencilState
&pipelineColorBlendStateCreateInfo, // pColorBlendState
&pipelineDynamicStateCreateInfo, // pDynamicState
*pipelineLayout, // layout
renderPass // renderPass
);
pipeline = GetContext()->GetDevice()->createGraphicsPipelineUnique(GetContext()->GetPipelineCache(), graphicsPipelineCreateInfo);
}

View File

@ -304,7 +304,7 @@ public:
descSetLayout = GetContext()->GetDevice()->createDescriptorSetLayoutUnique(
vk::DescriptorSetLayoutCreateInfo(vk::DescriptorSetLayoutCreateFlags(), ARRAY_SIZE(bindings), bindings));
pipelineLayout = GetContext()->GetDevice()->createPipelineLayoutUnique(
vk::PipelineLayoutCreateInfo(vk::PipelineLayoutCreateFlags(), 1, &descSetLayout.get(), 0, nullptr));
vk::PipelineLayoutCreateInfo(vk::PipelineLayoutCreateFlags(), 1, &descSetLayout.get()));
}
if (!sampler)
{
@ -360,5 +360,69 @@ private:
vk::UniquePipelineLayout pipelineLayout;
vk::UniqueDescriptorSetLayout descSetLayout;
ShaderManager *shaderManager;
SamplerManager *samplerManager;
};
class OSDPipeline
{
public:
void Init(ShaderManager *shaderManager, vk::ImageView imageView)
{
this->shaderManager = shaderManager;
if (!pipelineLayout)
{
vk::DescriptorSetLayoutBinding bindings[] = {
{ 0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment },// texture
};
descSetLayout = GetContext()->GetDevice()->createDescriptorSetLayoutUnique(
vk::DescriptorSetLayoutCreateInfo(vk::DescriptorSetLayoutCreateFlags(), ARRAY_SIZE(bindings), bindings));
pipelineLayout = GetContext()->GetDevice()->createPipelineLayoutUnique(
vk::PipelineLayoutCreateInfo(vk::PipelineLayoutCreateFlags(), 1, &descSetLayout.get()));
}
if (!sampler)
{
sampler = VulkanContext::Instance()->GetDevice()->createSamplerUnique(
vk::SamplerCreateInfo(vk::SamplerCreateFlags(), vk::Filter::eLinear, vk::Filter::eLinear,
vk::SamplerMipmapMode::eLinear, vk::SamplerAddressMode::eClampToEdge, vk::SamplerAddressMode::eClampToEdge,
vk::SamplerAddressMode::eClampToEdge, 0.0f, false, 16.0f, false,
vk::CompareOp::eNever, 0.0f, 0.0f, vk::BorderColor::eFloatOpaqueBlack));
}
if (GetContext()->GetRenderPass() != renderPass)
{
renderPass = GetContext()->GetRenderPass();
pipeline.reset();
}
if (!descriptorSet)
{
descriptorSet = std::move(GetContext()->GetDevice()->allocateDescriptorSetsUnique(
vk::DescriptorSetAllocateInfo(GetContext()->GetDescriptorPool(), 1, &descSetLayout.get())).front());
}
vk::DescriptorImageInfo imageInfo(*sampler, imageView, vk::ImageLayout::eShaderReadOnlyOptimal);
std::vector<vk::WriteDescriptorSet> writeDescriptorSets;
writeDescriptorSets.push_back(vk::WriteDescriptorSet(*descriptorSet, 0, 0, 1, vk::DescriptorType::eCombinedImageSampler, &imageInfo, nullptr, nullptr));
GetContext()->GetDevice()->updateDescriptorSets(writeDescriptorSets, nullptr);
}
vk::Pipeline GetPipeline()
{
if (!pipeline)
CreatePipeline();
return *pipeline;
}
void BindDescriptorSets(vk::CommandBuffer cmdBuffer) const
{
cmdBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, *pipelineLayout, 0, 1, &descriptorSet.get(), 0, nullptr);
}
private:
VulkanContext *GetContext() const { return VulkanContext::Instance(); }
void CreatePipeline();
vk::RenderPass renderPass;
vk::UniquePipeline pipeline;
vk::UniqueSampler sampler;
vk::UniqueDescriptorSet descriptorSet;
vk::UniquePipelineLayout pipelineLayout;
vk::UniqueDescriptorSetLayout descSetLayout;
ShaderManager *shaderManager;
};

View File

@ -324,6 +324,41 @@ void main()
}
)";
static const char OSDVertexShaderSource[] = R"(
#version 400
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
layout (location = 0) in vec4 inPos;
layout (location = 1) in uvec4 inColor;
layout (location = 2) in vec2 inUV;
layout (location = 0) out lowp vec4 outColor;
layout (location = 1) out mediump vec2 outUV;
void main()
{
outColor = inColor / 255.0;
outUV = inUV;
gl_Position = inPos;
}
)";
static const char OSDFragmentShaderSource[] = R"(
#version 400
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
layout (binding = 0) uniform sampler2D tex;
layout (location = 0) in lowp vec4 inColor;
layout (location = 1) in mediump vec2 inUV;
layout (location = 0) out vec4 FragColor;
void main()
{
FragColor = inColor * texture(tex, inUV);
}
)";
static const TBuiltInResource DefaultTBuiltInResource = {
/* .MaxLights = */ 32,
/* .MaxClipPlanes = */ 6,
@ -540,3 +575,13 @@ vk::UniqueShaderModule ShaderManager::compileQuadFragmentShader()
{
return createShaderModule(VulkanContext::Instance()->GetDevice(), vk::ShaderStageFlagBits::eFragment, QuadFragmentShaderSource);
}
vk::UniqueShaderModule ShaderManager::compileOSDVertexShader()
{
return createShaderModule(VulkanContext::Instance()->GetDevice(), vk::ShaderStageFlagBits::eVertex, OSDVertexShaderSource);
}
vk::UniqueShaderModule ShaderManager::compileOSDFragmentShader()
{
return createShaderModule(VulkanContext::Instance()->GetDevice(), vk::ShaderStageFlagBits::eFragment, OSDFragmentShaderSource);
}

View File

@ -111,6 +111,18 @@ public:
quadFragmentShader = compileQuadFragmentShader();
return *quadFragmentShader;
}
vk::ShaderModule GetOSDVertexShader()
{
if (!osdVertexShader)
osdVertexShader = compileOSDVertexShader();
return *osdVertexShader;
}
vk::ShaderModule GetOSDFragmentShader()
{
if (!osdFragmentShader)
osdFragmentShader = compileOSDFragmentShader();
return *osdFragmentShader;
}
private:
template<typename T>
@ -128,6 +140,8 @@ private:
vk::UniqueShaderModule compileModVolFragmentShader();
vk::UniqueShaderModule compileQuadVertexShader();
vk::UniqueShaderModule compileQuadFragmentShader();
vk::UniqueShaderModule compileOSDVertexShader();
vk::UniqueShaderModule compileOSDFragmentShader();
std::map<u32, vk::UniqueShaderModule> vertexShaders;
std::map<u32, vk::UniqueShaderModule> fragmentShaders;
@ -135,4 +149,6 @@ private:
vk::UniqueShaderModule modVolShader;
vk::UniqueShaderModule quadVertexShader;
vk::UniqueShaderModule quadFragmentShader;
vk::UniqueShaderModule osdVertexShader;
vk::UniqueShaderModule osdFragmentShader;
};

View File

@ -235,6 +235,7 @@ void Texture::CreateImage(vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk:
void Texture::SetImage(u32 srcSize, void *srcData, bool isNew)
{
verify((bool)commandBuffer);
commandBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
if (!isNew && !needsStaging)

View File

@ -29,9 +29,6 @@ void setImageLayout(vk::CommandBuffer const& commandBuffer, vk::Image image, vk:
struct Texture : BaseTextureCacheData
{
Texture(vk::PhysicalDevice physicalDevice, vk::Device device, VulkanAllocator *allocator = nullptr)
: physicalDevice(physicalDevice), device(device), format(vk::Format::eUndefined), allocator(allocator)
{}
~Texture() override
{
imageView.reset();
@ -47,6 +44,10 @@ struct Texture : BaseTextureCacheData
void SetCommandBuffer(vk::CommandBuffer commandBuffer) { this->commandBuffer = commandBuffer; }
virtual bool Force32BitTexture(TextureType type) override { return !VulkanContext::Instance()->IsFormatSupported(type); }
void SetAllocator(VulkanAllocator *allocator) { this->allocator = allocator; }
void SetPhysicalDevice(vk::PhysicalDevice physicalDevice) { this->physicalDevice = physicalDevice; }
void SetDevice(vk::Device device) { this->device = device; }
private:
void Init(u32 width, u32 height, vk::Format format);
void SetImage(u32 size, void *data, bool isNew);
@ -54,7 +55,7 @@ private:
vk::MemoryPropertyFlags memoryProperties, vk::ImageAspectFlags aspectMask);
void GenerateMipmaps();
vk::Format format;
vk::Format format = vk::Format::eUndefined;
vk::Extent2D extent;
u32 mipmapLevels = 1;
bool needsStaging = false;
@ -67,7 +68,7 @@ private:
vk::PhysicalDevice physicalDevice;
vk::Device device;
VulkanAllocator *allocator;
VulkanAllocator *allocator = nullptr;
vk::DeviceMemory sharedDeviceMemory;
u32 memoryType = 0;
vk::DeviceSize memoryOffset = 0;
@ -129,3 +130,7 @@ private:
vk::PhysicalDevice physicalDevice;
vk::Device device;
};
class TextureCache : public BaseTextureCache<Texture>
{
};

View File

@ -26,7 +26,8 @@
#include "commandpool.h"
#include "drawer.h"
#include "shaders.h"
#include "../gui.h"
#include "rend/gui.h"
#include "rend/osd.h"
extern bool ProcessFrame(TA_context* ctx);
@ -41,14 +42,55 @@ public:
// FIXME this might be called after initial init
texAllocator.SetChunkSize(16 * 1024 * 1024);
while (textureDrawer.size() < 2)
textureDrawer.emplace_back();
textureDrawer[0].Init(&samplerManager, &shaderManager, &texAllocator);
textureDrawer[0].SetCommandPool(&texCommandPool);
textureDrawer[1].Init(&samplerManager, &shaderManager, &texAllocator);
textureDrawer[1].SetCommandPool(&texCommandPool);
rttPipelineManager.Init(&shaderManager);
if (textureDrawer.size() > GetContext()->GetSwapChainSize())
textureDrawer.resize(GetContext()->GetSwapChainSize());
else
{
while (textureDrawer.size() < GetContext()->GetSwapChainSize())
textureDrawer.emplace_back();
}
for (auto& drawer : textureDrawer)
{
drawer.Init(&samplerManager, &texAllocator, &rttPipelineManager, &textureCache);
drawer.SetCommandPool(&texCommandPool);
}
screenDrawer.Init(&samplerManager, &shaderManager);
quadPipeline.Init(&shaderManager);
#ifdef __ANDROID__
if (!vjoyTexture)
{
int w, h;
u8 *image_data = loadPNGData(get_readonly_data_path(DATA_PATH "buttons.png"), w, h);
if (image_data == nullptr)
{
WARN_LOG(RENDERER, "Cannot load buttons.png image");
}
else
{
vjoyTexture = std::unique_ptr<Texture>(new Texture());
vjoyTexture->tex_type = TextureType::_8888;
vjoyTexture->tcw.full = 0;
vjoyTexture->tsp.full = 0;
vjoyTexture->SetAllocator(&texAllocator);
vjoyTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice());
vjoyTexture->SetDevice(*GetContext()->GetDevice());
vjoyTexture->SetCommandBuffer(texCommandPool.Allocate());
vjoyTexture->UploadToGPU(OSD_TEX_W, OSD_TEX_H, image_data);
vjoyTexture->SetCommandBuffer(nullptr);
delete [] image_data;
osdPipeline.Init(&shaderManager, vjoyTexture->GetImageView());
}
}
if (!osdBuffer)
{
osdBuffer = std::unique_ptr<BufferData>(new BufferData(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice().get(),
sizeof(OSDVertex) * VJOY_VISIBLE * 4,
vk::BufferUsageFlagBits::eVertexBuffer, &texAllocator));
}
#endif
return true;
}
@ -58,13 +100,14 @@ public:
texCommandPool.Init();
screenDrawer.Init(&samplerManager, &shaderManager);
quadPipeline.Init(&shaderManager);
osdPipeline.Init(&shaderManager, vjoyTexture->GetImageView());
}
void Term() override
{
DEBUG_LOG(RENDERER, "VulkanRenderer::Term");
GetContext()->WaitIdle();
killtex();
textureCache.Clear();
fogTexture = nullptr;
texCommandPool.Term();
shaderManager.Term();
@ -86,10 +129,13 @@ public:
std::unique_ptr<Texture>& curTexture = framebufferTextures[GetContext()->GetCurrentImageIndex()];
if (!curTexture)
{
curTexture = std::unique_ptr<Texture>(new Texture(GetContext()->GetPhysicalDevice(), *GetContext()->GetDevice(), &texAllocator));
curTexture = std::unique_ptr<Texture>(new Texture());
curTexture->tex_type = TextureType::_8888;
curTexture->tcw.full = 0;
curTexture->tsp.full = 0;
curTexture->SetAllocator(&texAllocator);
curTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice());
curTexture->SetDevice(*GetContext()->GetDevice());
}
curTexture->SetCommandBuffer(texCommandPool.Allocate());
curTexture->UploadToGPU(width, height, (u8*)pb.data());
@ -117,6 +163,8 @@ public:
quadPipeline.SetTexture(curTexture.get());
quadPipeline.BindDescriptorSets(cmdBuffer);
float blendConstants[4] = { 1.0, 1.0, 1.0, 1.0 };
cmdBuffer.setBlendConstants(blendConstants);
// FIXME scaling, stretching...
vk::Viewport viewport(ds2s_offs_x, 0.f, screen_width - ds2s_offs_x * 2, (float)screen_height);
cmdBuffer.setViewport(0, 1, &viewport);
@ -138,7 +186,19 @@ public:
return RenderFramebuffer();
}
bool result = ProcessFrame(ctx);
ctx->rend_inuse.Lock();
if (KillTex)
textureCache.Clear();
bool result = ta_parse_vdrc(ctx);
textureCache.CollectCleanup();
if (ctx->rend.Overrun)
WARN_LOG(PVR, "ERROR: TA context overrun");
result = result && !ctx->rend.Overrun;
if (result)
CheckFogTexture();
@ -151,6 +211,51 @@ public:
void DrawOSD(bool clear_screen) override
{
if (!vjoyTexture)
return;
if (clear_screen)
{
GetContext()->NewFrame();
GetContext()->BeginRenderPass();
}
const float screen_stretching = settings.rend.ScreenStretching / 100.f;
float dc2s_scale_h, ds2s_offs_x;
if (settings.rend.Rotate90)
{
dc2s_scale_h = screen_height / 640.0f;
ds2s_offs_x = (screen_width - dc2s_scale_h * 480.0f * screen_stretching) / 2;
}
else
{
dc2s_scale_h = screen_height / 480.0f;
ds2s_offs_x = (screen_width - dc2s_scale_h * 640.0f * screen_stretching) / 2;
}
std::vector<OSDVertex> osdVertices = GetOSDVertices();
const float x1 = 2.0f / (screen_width / dc2s_scale_h /* FIXME * scale_x */) * screen_stretching;
const float y1 = 2.0f / 480 /* FIXME dc_height */;
const float x2 = 1 - 2 * ds2s_offs_x / screen_width;
const float y2 = 1;
for (OSDVertex& vtx : osdVertices)
{
vtx.x = vtx.x * x1 - x2;
vtx.y = vtx.y * y1 - y2;
}
const vk::CommandBuffer cmdBuffer = GetContext()->GetCurrentCommandBuffer();
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, osdPipeline.GetPipeline());
osdPipeline.BindDescriptorSets(cmdBuffer);
const vk::Viewport viewport(0, 0, (float)screen_width, (float)screen_height, 0, 1.f);
cmdBuffer.setViewport(0, 1, &viewport);
const vk::Rect2D scissor({ 0, 0 }, { (u32)screen_width, (u32)screen_height });
cmdBuffer.setScissor(0, 1, &scissor);
osdBuffer->upload(*GetContext()->GetDevice(), osdVertices.size() * sizeof(OSDVertex), osdVertices.data());
const vk::DeviceSize zero = 0;
cmdBuffer.bindVertexBuffers(0, 1, &osdBuffer->buffer.get(), &zero);
for (int i = 0; i < osdVertices.size(); i += 4)
cmdBuffer.draw(4, 1, i, 0);
if (clear_screen)
GetContext()->EndFrame();
}
bool Render() override
@ -158,14 +263,18 @@ public:
if (pvrrc.isRenderFramebuffer)
return true;
Drawer *drawer;
if (pvrrc.isRTT)
{
textureDrawer[curTextureDrawer].Draw(fogTexture.get());
curTextureDrawer ^= 1;
return false;
}
drawer = &textureDrawer[GetContext()->GetCurrentImageIndex()];
else
return screenDrawer.Draw(fogTexture.get());
drawer = &screenDrawer;
drawer->Draw(fogTexture.get());
if (!pvrrc.isRTT)
DrawOSD(false);
drawer->EndRenderPass();
return !pvrrc.isRTT;
}
void Present() override
@ -175,12 +284,15 @@ public:
virtual u64 GetTexture(TSP tsp, TCW tcw) override
{
Texture* tf = static_cast<Texture*>(getTextureCacheData(tsp, tcw, [this](){
return (BaseTextureCacheData *)new Texture(VulkanContext::Instance()->GetPhysicalDevice(), *VulkanContext::Instance()->GetDevice(), &this->texAllocator);
}));
Texture* tf = textureCache.getTextureCacheData(tsp, tcw);
if (tf->IsNew())
{
tf->Create();
tf->SetAllocator(&texAllocator);
tf->SetPhysicalDevice(GetContext()->GetPhysicalDevice());
tf->SetDevice(*GetContext()->GetDevice());
}
//update if needed
if (tf->NeedsUpdate())
@ -202,7 +314,10 @@ private:
{
if (!fogTexture)
{
fogTexture = std::unique_ptr<Texture>(new Texture(GetContext()->GetPhysicalDevice(), *GetContext()->GetDevice()));
fogTexture = std::unique_ptr<Texture>(new Texture());
fogTexture->SetAllocator(&texAllocator);
fogTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice());
fogTexture->SetDevice(*GetContext()->GetDevice());
fogTexture->tex_type = TextureType::_8;
fog_needs_update = true;
}
@ -224,11 +339,15 @@ private:
SamplerManager samplerManager;
ShaderManager shaderManager;
ScreenDrawer screenDrawer;
std::vector<TextureDrawer> textureDrawer; // FIXME should be a singleton
int curTextureDrawer = 0;
RttPipelineManager rttPipelineManager;
std::vector<TextureDrawer> textureDrawer;
VulkanAllocator texAllocator;
std::vector<std::unique_ptr<Texture>> framebufferTextures;
QuadPipeline quadPipeline;
OSDPipeline osdPipeline;
std::unique_ptr<Texture> vjoyTexture;
std::unique_ptr<BufferData> osdBuffer;
TextureCache textureCache;
};
Renderer* rend_Vulkan()

View File

@ -258,6 +258,7 @@
AE8C274221122E2500D4D8F4 /* License.txt in Resources */ = {isa = PBXBuildFile; fileRef = AE8C273C21122E2500D4D8F4 /* License.txt */; };
AE8C274321122E2500D4D8F4 /* xbrz.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE8C273D21122E2500D4D8F4 /* xbrz.cpp */; };
AE90679B235B6F6400CE473C /* gl_context.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE90679A235B6F6400CE473C /* gl_context.cpp */; };
AE90679D235DF80400CE473C /* osd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE90679C235DF80400CE473C /* osd.cpp */; };
AED73BAE22FC0E9600ECDB64 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = AED73BAD22FC0E9600ECDB64 /* README.md */; };
AED73DC42303E19200ECDB64 /* sdl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AED73DC02303E19100ECDB64 /* sdl.cpp */; };
AED73DC72303E57C00ECDB64 /* libSDL2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AED73DC62303E57C00ECDB64 /* libSDL2.a */; };
@ -850,6 +851,7 @@
AE8C273F21122E2500D4D8F4 /* xbrz_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbrz_config.h; sourceTree = "<group>"; };
AE8C274021122E2500D4D8F4 /* xbrz_tools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbrz_tools.h; sourceTree = "<group>"; };
AE90679A235B6F6400CE473C /* gl_context.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gl_context.cpp; sourceTree = "<group>"; };
AE90679C235DF80400CE473C /* osd.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = osd.cpp; sourceTree = "<group>"; };
AED73BAD22FC0E9600ECDB64 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../../../core/README.md; sourceTree = "<group>"; };
AED73DC02303E19100ECDB64 /* sdl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sdl.cpp; sourceTree = "<group>"; };
AED73DC12303E19100ECDB64 /* sdl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sdl.h; sourceTree = "<group>"; };
@ -1858,6 +1860,7 @@
AEE62769220D7B5500EC7E89 /* gui.h */,
AEE6279222247C0A00EC7E89 /* gui_util.cpp */,
AEE6279322247C0A00EC7E89 /* gui_util.h */,
AE90679C235DF80400CE473C /* osd.cpp */,
84B7BEA21B72720200F9733F /* rend.h */,
AED73EAD2348E49900ECDB64 /* sorter.cpp */,
AED73EBD2348E49900ECDB64 /* sorter.h */,
@ -2826,6 +2829,7 @@
84B7BF201B72720200F9733F /* uncompr.c in Sources */,
AE649C2A218C553A00EF4A81 /* LzFind.c in Sources */,
84B7BF2A1B72720200F9733F /* arm7.cpp in Sources */,
AE90679D235DF80400CE473C /* osd.cpp in Sources */,
84B7BF0D1B72720200F9733F /* zip_source_zip.c in Sources */,
84B7BEF51B72720200F9733F /* zip_fopen.c in Sources */,
84B7BF551B72720200F9733F /* sh4_mem.cpp in Sources */,