/**********************************************************************
This file is part of Crack dot Com's free source code release of
Golgotha.
for
information about compiling & licensing issues visit this URL
If that doesn't help, contact Jonathan Clark at
golgotha_source@usa.net (Subject should have "GOLG" in it)
***********************************************************************/
#include "video/glide/glide.h"
#include "glide/gr_vram.hh"
#include "time/profile.hh"
#include "r1_res.hh"
r1_glide_vram_class *r1_glide_vram_class_instance=0;
i4_profile_class pf_glide_install_vram("glide install vram texture");
i4_profile_class pf_glide_free_vram("glide free vram texture");
r1_glide_vram_class::r1_glide_vram_class(const i4_pal *pal)
: r1_texture_manager_class(pal),finished_array(16,16)
{
tex_heap_man = 0;
tex_heap_man_2 = 0;
r1_glide_vram_class_instance = this;
r1_glide_vram_class::init();
min_texture_dimention = 1;
max_texture_dimention = r1_max_texture_size;
if (max_texture_dimention>256)
max_texture_dimention=256; // glide can't go higher than 256
regular_format.red_mask = 31 << 11;
regular_format.green_mask = 63 << 5;
regular_format.blue_mask = 31;
regular_format.alpha_mask = 0;
regular_format.lookup = 0;
regular_format.calc_shift();
regular_format.pixel_depth = I4_16BIT;
chroma_format.red_mask = 31 << 10;
chroma_format.green_mask = 31 << 5;
chroma_format.blue_mask = 31;
chroma_format.alpha_mask = 1 << 15;
chroma_format.lookup = 0;
chroma_format.calc_shift();
chroma_format.pixel_depth = I4_16BIT;
alpha_format.red_mask = 15 << 8;
alpha_format.green_mask = 15 << 4;
alpha_format.blue_mask = 15;
alpha_format.alpha_mask = 15 << 12;
alpha_format.lookup = 0;
alpha_format.calc_shift();
alpha_format.pixel_depth = I4_16BIT;
}
void r1_glide_vram_class::init()
{
r1_texture_manager_class::init();
int t_vram=grTexMaxAddress(GR_TMU0) - grTexMinAddress(GR_TMU0);
int heap1_size=t_vram > 2*1024*1024 ? 2*1024*1024 : t_vram;
int heap2_size=t_vram - heap1_size;
heap1_address=grTexMinAddress(GR_TMU0);
heap2_address=heap1_address + heap1_size;
tex_heap_man = new r1_texture_heap_class(heap1_size,
heap1_address,
sizeof(free_node),
sizeof(used_node),
&frame_count);
/* if (heap2_size>0)
tex_heap_man_2 = new r1_texture_heap_class(heap2_size,
heap2_address,
sizeof(free_node),
sizeof(used_node),
&frame_count);
*/
array_lock.lock();
array_lock.unlock();
bytes_loaded = 0;
textures_loaded = 0;
}
void r1_glide_vram_class::uninit()
{
if (tex_heap_man)
{
delete tex_heap_man;
tex_heap_man = 0;
}
if (tex_heap_man_2)
{
delete tex_heap_man_2;
tex_heap_man_2 = 0;
}
r1_texture_manager_class::uninit();
}
i4_bool r1_glide_vram_class::immediate_mip_load(r1_mip_load_info *load_info)
{
if (textures_loaded > 0)
{
if (load_info->dest_mip->last_frame_used != -1)
{
load_info->error = R1_MIP_LOAD_BUSY;
return i4_F;
}
}
pf_glide_install_vram.start();
r1_miplevel_t *mip = load_info->dest_mip;
sw32 actual_w,actual_h;
w32 w_lod=GR_LOD_1, h_lod=GR_LOD_1;
//determine the aspect ratio and level of detail
for (actual_w=min_texture_dimention; actual_wwidth; actual_w*=2)
w_lod--;
for (actual_h=min_texture_dimention; actual_hheight; actual_h*=2)
h_lod--;
w32 need_size = ((actual_w * actual_h * 2) + 7) & (~7); // sizes must be 8 byte alligned
used_node *new_used = (used_node *)tex_heap_man->alloc(need_size);
if (!new_used && tex_heap_man_2)
new_used = (used_node *)tex_heap_man_2->alloc(need_size);
if (!new_used)
{
pf_glide_install_vram.stop();
load_info->error = R1_MIP_LOAD_NO_ROOM;
return i4_F;
}
free_node *f = (free_node *)new_used->node;
//f is the node we'll use
if (load_info->src_file)
{
load_info->src_file->read(scratch_texture,mip->width*mip->height*2);
}
else
{
i4_str *fn = r1_texture_id_to_filename(mip->entry->id, r1_get_decompressed_dir());
i4_file_class *fp = i4_open(*fn);
delete fn;
fp->seek(mip->entry->file_offsets[mip->level]+8);
fp->read(scratch_texture,mip->width*mip->height*2);
delete fp;
}
bytes_loaded += mip->width*mip->height*2;
textures_loaded++;
//fill these data fields w/the appropriate data
f->mip = mip;
mip->vram_handle = new_used;
if (mip->last_frame_used != -1)
mip->last_frame_used = frame_count;
// setup the correct lod (level of detail) and aspect variables
if (actual_w>=actual_h)
{
// uv correction for this texture because we made it pow2
f->smul = (i4_float)(mip->width) / (i4_float)actual_w * 256.0;
f->tmul = (i4_float)(mip->height) / (i4_float)actual_w * 256.0;
f->lod = w_lod;
f->aspect = GR_ASPECT_1x1 - (h_lod-w_lod);
}
else
{
f->smul = (i4_float)(mip->width) / (i4_float)actual_h * 256.0;
f->tmul = (i4_float)(mip->height) / (i4_float)actual_h * 256.0;
f->lod = h_lod;
f->aspect = w_lod-h_lod+GR_ASPECT_1x1;
}
GrTexInfo tex_load_info;
tex_load_info.smallLod = f->lod;
tex_load_info.largeLod = f->lod;
tex_load_info.aspectRatio = f->aspect;
tex_load_info.format = GR_TEXFMT_RGB_565;
// download the texture to the card
grTexDownloadMipMapLevel(GR_TMU0,
f->start,
f->lod,
f->lod,
f->aspect,
GR_TEXFMT_RGB_565,
GR_MIPMAPLEVELMASK_BOTH,
scratch_texture);
pf_glide_install_vram.stop();
return i4_T;
}
//typedef void (*async_callback)(w32 count, void *context);
void glide_async_callback(w32 count, void *context)
{
r1_glide_vram_class::used_node *u = (r1_glide_vram_class::used_node *)context;
r1_glide_vram_class::free_node *f = (r1_glide_vram_class::free_node *)u->node;
if (f->async_fp)
delete f->async_fp;
f->async_fp = 0;
r1_glide_vram_class_instance->async_load_finished(u);
}
void r1_glide_vram_class::async_load_finished(used_node *u)
{
array_lock.lock();
finished_array.add(u);
array_lock.unlock();
}
i4_bool r1_glide_vram_class::async_mip_load(r1_mip_load_info *load_info)
{
//return immediate_mip_load(load_info);
if (bytes_loaded > 32768 || textures_loaded > 16)
{
if (load_info->dest_mip->last_frame_used != -1)
{
load_info->error = R1_MIP_LOAD_BUSY;
return i4_F;
}
}
pf_glide_install_vram.start();
r1_miplevel_t *mip = load_info->dest_mip;
sw32 actual_w,actual_h;
w32 w_lod=GR_LOD_1, h_lod=GR_LOD_1;
//determine the aspect ratio and level of detail
for (actual_w=min_texture_dimention; actual_wwidth; actual_w*=2)
w_lod--;
for (actual_h=min_texture_dimention; actual_hheight; actual_h*=2)
h_lod--;
w32 need_size = ((actual_w * actual_h * 2) + 7) & (~7); // sizes must be 8 byte alligned
used_node *new_used = (used_node *)tex_heap_man->alloc(need_size,R1_TEX_HEAP_DONT_LIST);
if (!new_used && tex_heap_man_2)
new_used = (used_node *)tex_heap_man_2->alloc(need_size,R1_TEX_HEAP_DONT_LIST);
if (!new_used)
{
pf_glide_install_vram.stop();
load_info->error = R1_MIP_LOAD_NO_ROOM;
return i4_F;
}
//f is the node we'll use
free_node *f = (free_node *)new_used->node;
//fill these data fields w/the appropriate data
f->mip = mip;
f->data = (w8 *)i4_malloc(need_size,"temp glide texture");
f->async_fp = 0;
mip->flags |= R1_MIPLEVEL_IS_LOADING;
// setup the correct lod (level of detail) and aspect variables
if (actual_w>=actual_h)
{
// uv correction for this texture because we made it pow2
f->smul = (i4_float)(mip->width) / (i4_float)actual_w * 256.0;
f->tmul = (i4_float)(mip->height) / (i4_float)actual_w * 256.0;
f->lod = w_lod;
f->aspect = GR_ASPECT_1x1 - (h_lod-w_lod);
}
else
{
f->smul = (i4_float)(mip->width) / (i4_float)actual_h * 256.0;
f->tmul = (i4_float)(mip->height) / (i4_float)actual_h * 256.0;
f->lod = h_lod;
f->aspect = w_lod-h_lod+GR_ASPECT_1x1;
}
i4_bool async_worked;
if (load_info->src_file)
{
texture_resolution_changed=i4_T;
async_worked = load_info->src_file->async_read(f->data,mip->width*mip->height*2,glide_async_callback,new_used);
}
else
{
i4_str *fn = r1_texture_id_to_filename(mip->entry->id, r1_get_decompressed_dir());
i4_file_class *fp = i4_open(*fn,I4_READ | I4_NO_BUFFER | I4_SUPPORT_ASYNC);
delete fn;
if (!fp)
async_worked = i4_F;
else
{
f->async_fp = fp;
fp->seek(mip->entry->file_offsets[mip->level]+8);
texture_resolution_changed=i4_T;
async_worked = fp->async_read(f->data,mip->width*mip->height*2,glide_async_callback,new_used);
}
}
if (!async_worked)
{
if (f->async_fp)
delete f->async_fp;
if (f->data)
i4_free(f->data);
mip->flags &= (~R1_MIPLEVEL_IS_LOADING);
if (new_used->node->start>=heap2_address)
tex_heap_man_2->free((r1_tex_heap_used_node *)new_used);
else
tex_heap_man->free((r1_tex_heap_used_node *)new_used);
load_info->error = R1_MIP_LOAD_BUSY;
pf_glide_install_vram.stop();
return i4_F;
}
bytes_loaded += mip->width*mip->height*2;
textures_loaded++;
pf_glide_install_vram.stop();
return i4_T;
}
void r1_glide_vram_class::next_frame()
{
r1_texture_manager_class::next_frame();
sw32 i;
array_lock.lock();
for (i=0; inode;
//this officially puts it in vram
f->mip->vram_handle = u;
f->mip->flags &= (~R1_MIPLEVEL_IS_LOADING);
if (f->mip->last_frame_used != -1)
f->mip->last_frame_used = frame_count;
if (u->node->start>=heap2_address)
tex_heap_man_2->update_usage((r1_tex_heap_used_node *)u);
else
//this adds it into the list of used nodes
tex_heap_man->update_usage((r1_tex_heap_used_node *)u);
GrTexInfo tex_load_info;
tex_load_info.smallLod = f->lod;
tex_load_info.largeLod = f->lod;
tex_load_info.aspectRatio = f->aspect;
tex_load_info.format = GR_TEXFMT_RGB_565;
// download the texture to the card
grTexDownloadMipMapLevel(GR_TMU0,
f->start,
f->lod,
f->lod,
f->aspect,
GR_TEXFMT_RGB_565,
GR_MIPMAPLEVELMASK_BOTH,
f->data);
i4_free(f->data);
f->data = 0;
}
finished_array.clear();
array_lock.unlock();
if (tex_heap_man->needs_cleanup)
tex_heap_man->free_really_old();
if (tex_heap_man_2 && tex_heap_man_2->needs_cleanup)
tex_heap_man_2->free_really_old();
bytes_loaded = 0;
textures_loaded = 0;
}
void r1_glide_vram_class::free_mip(r1_vram_handle_type vram_handle)
{
pf_glide_free_vram.start();
r1_tex_heap_used_node *n=(r1_tex_heap_used_node *)vram_handle;
if (n->node->start>=heap2_address)
tex_heap_man_2->free(n);
else
tex_heap_man->free(n);
pf_glide_free_vram.stop();
}
void r1_glide_vram_class::select_texture(r1_vram_handle_type handle,
float &smul, float &tmul,
i4_bool holy, i4_bool alpha)
{
free_node *n = (free_node *)((used_node *)handle)->node;
GrTexInfo tex_source_info;
tex_source_info.smallLod = n->lod;
tex_source_info.largeLod = n->lod;
tex_source_info.aspectRatio = n->aspect;
if (alpha)
tex_source_info.format = GR_TEXFMT_ARGB_4444;
else
if (holy)
tex_source_info.format = GR_TEXFMT_ARGB_1555;
else
tex_source_info.format = GR_TEXFMT_RGB_565;
smul = n->smul;
tmul = n->tmul;
grTexSource(GR_TMU0,
n->start,
GR_MIPMAPLEVELMASK_BOTH,
&tex_source_info);
}
r1_miplevel_t *r1_glide_vram_class::get_texture(r1_texture_handle handle,
w32 frame_counter,
sw32 desired_width,
sw32 &w, sw32 &h)
{
r1_miplevel_t *mip = r1_texture_manager_class::get_texture(handle,frame_counter,desired_width,w,h);
if (mip)
{
r1_tex_heap_used_node *u = (r1_tex_heap_used_node *)mip->vram_handle;
if (u)
{
if (u->node->start>=heap2_address)
tex_heap_man_2->update_usage(u);
else
tex_heap_man->update_usage(u);
}
}
return mip;
}