OpenGL: a polygon drawn outside its clipping rectangle is, and its state set only when it changes

The per-triangle and per-strip renderer's program discards what is
inside the clipping rectangle when the polygon asks to be drawn outside
it, as the per-pixel one's does: it compared the flag with -1, which it
never is, and drew the whole polygon. tools/gl/gl_clip.sh draws three
such polygons of one program, the middle one's rectangle another, on
each renderer with OpenGL and OpenGL ES (the test disc built with
-DLIVE_CLIP: tools/threads/live_disc.py --clip), and is run in CI.

That renderer sets a polygon's trilinear blend, palette and clipping
uniforms only when they differ from what its program was last given,
and its textures' LOD bias and anisotropy only when they differ from
what the texture has. The texture parameters are kept by texture name
in a table, not a map, and every texture deleted goes through the cache.
This commit is contained in:
libretroadmin committed 2026-11-01 19:43:47 +01:00
1 parent 03618ee948
commit 022b9c3d1f
11 files changed
+679 -47

No files matched your search

+6
View File
@@ -21,6 +21,8 @@ name: Vulkan negotiation
# them, with Vulkan and with OpenGL (tools/vulkan/vk_dma.sh,
# tools/gl/gl_dma.sh: Mesa's surfaceless EGL). And the translucent
# sorter's sort against a stable sort by < (tools/rend/sort_pairs_test.sh).
# And polygons drawn outside a clipping rectangle, with OpenGL and OpenGL
# ES, each renderer (tools/gl/gl_clip.sh).
on:
push:
@@ -50,6 +52,7 @@ on:
- 'core/rend/sort_pairs.h'
- 'core/rend/grow_buf.h'
- 'tools/rend/sort_pairs_test.*'
- 'tools/rend/clip_check.py'
- 'tools/threads/live_prog.c'
- 'Makefile*'
- '.github/workflows/vulkan-negotiation.yml'
@@ -80,6 +83,7 @@ on:
- 'core/rend/sort_pairs.h'
- 'core/rend/grow_buf.h'
- 'tools/rend/sort_pairs_test.*'
- 'tools/rend/clip_check.py'
- 'tools/threads/live_prog.c'
- 'Makefile*'
- '.github/workflows/vulkan-negotiation.yml'
@@ -109,6 +113,8 @@ jobs:
sh tools/rend/rtt_watch_test.sh
VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.json sh tools/vulkan/vk_dma.sh
sh tools/gl/gl_dma.sh
- name: Polygons drawn outside a clipping rectangle, with OpenGL and OpenGL ES (each renderer)
run: sh tools/gl/gl_clip.sh
- name: The translucent sorter's sort (radix, stable, as a sort by <)
run: sh tools/rend/sort_pairs_test.sh
- name: The temporal upscaler's side in the core (strips matched, the request, the inputs)
+2 -2
View File
@@ -594,9 +594,9 @@ static void gl_term(void)
glDeleteProgram(g.program);
g.program = 0;
}
glDeleteTextures(1, &fbTextureId);
glcache.DeleteTextures(1, &fbTextureId);
fbTextureId = 0;
glDeleteTextures(1, &fogTextureId);
glcache.DeleteTextures(1, &fogTextureId);
fogTextureId = 0;
glcache.DeleteTextures(1, &paletteTextureId);
paletteTextureId = 0;
+1 -1
View File
@@ -294,7 +294,7 @@ void GLTexDecoder::Term()
if (ring != 0)
glDeleteBuffers(1, &ring);
if (ringTexture != 0)
glDeleteTextures(1, &ringTexture);
glcache.DeleteTextures(1, &ringTexture);
if (framebuffer != 0)
glDeleteFramebuffers(1, &framebuffer);
if (vertexArray != 0)
+71 -29
View File
@@ -1,5 +1,6 @@
#pragma once
#include <map>
#include <string.h>
#include "rend/grow_buf.h"
#define TEXTURE_ID_CACHE_SIZE 32
@@ -46,7 +47,9 @@ public:
void DeleteTextures(GLsizei n, const GLuint *textures) {
for (int i = 0; i < n; i++)
{
_texture_params.erase(textures[i]);
TextureParameters *params = Params(textures[i]);
if (params != nullptr)
memset(params, 0xFF, sizeof(*params));
if (textures[i] == _texture)
_texture = 0;
}
@@ -185,35 +188,51 @@ public:
void TexParameteri(GLenum target, GLenum pname, GLint param)
{
if (target == GL_TEXTURE_2D && !_disable_cache)
TextureParameters *cur_params = target == GL_TEXTURE_2D ? Params(_texture) : nullptr;
if (cur_params != nullptr)
{
TextureParameters &cur_params = _texture_params[_texture];
GLenum *field = nullptr;
switch (pname) {
case GL_TEXTURE_MIN_FILTER:
if (cur_params._min_filter == (GLenum)param)
case GL_TEXTURE_MIN_FILTER: field = &cur_params->_min_filter; break;
case GL_TEXTURE_MAG_FILTER: field = &cur_params->_mag_filter; break;
case GL_TEXTURE_WRAP_S: field = &cur_params->_wrap_s; break;
case GL_TEXTURE_WRAP_T: field = &cur_params->_wrap_t; break;
}
if (field != nullptr)
{
if (*field == (GLenum)param)
return;
cur_params._min_filter = param;
break;
case GL_TEXTURE_MAG_FILTER:
if (cur_params._mag_filter == (GLenum)param)
return;
cur_params._mag_filter = param;
break;
case GL_TEXTURE_WRAP_S:
if (cur_params._wrap_s == (GLenum)param)
return;
cur_params._wrap_s = param;
break;
case GL_TEXTURE_WRAP_T:
if (cur_params._wrap_t == (GLenum)param)
return;
cur_params._wrap_t = param;
break;
*field = param;
}
}
glTexParameteri(target, pname, param);
}
/* The same for the parameters that are floats (bit for bit) */
void TexParameterf(GLenum target, GLenum pname, GLfloat param)
{
TextureParameters *cur_params = target == GL_TEXTURE_2D ? Params(_texture) : nullptr;
if (cur_params != nullptr)
{
GLfloat *field = nullptr;
#ifdef GL_TEXTURE_LOD_BIAS
if (pname == GL_TEXTURE_LOD_BIAS)
field = &cur_params->_lod_bias;
#endif
#ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
if (pname == GL_TEXTURE_MAX_ANISOTROPY_EXT)
field = &cur_params->_max_anisotropy;
#endif
if (field != nullptr)
{
if (memcmp(field, &param, sizeof(param)) == 0)
return;
*field = param;
}
}
glTexParameterf(target, pname, param);
}
GLuint GenTexture() {
if (_texture_cache_size == 0) {
_texture_cache_size = TEXTURE_ID_CACHE_SIZE;
@@ -224,7 +243,8 @@ public:
void Reset() {
_texture = 0xFFFFFFFFu;
_texture_params.clear();
if (_texture_params.data != nullptr)
memset(_texture_params.data, 0xFF, _texture_params.size);
_src_blend_factor = 0xFFFFFFFFu;
_dst_blend_factor = 0xFFFFFFFFu;
_clear_r = -1.f;
@@ -271,15 +291,37 @@ public:
}
private:
class TextureParameters {
public:
TextureParameters() : _min_filter(0xFFFFFFFFu), _mag_filter(0xFFFFFFFFu), _wrap_s(0xFFFFFFFFu), _wrap_t(0xFFFFFFFFu) {}
/* A texture's parameters as last set: by its name, in a table that
* grows (names are small numbers; one past MaxCachedName is not cached).
* All bits set is none yet: no enum is that, and the floats are NaN,
* which compares with nothing. */
struct TextureParameters {
GLenum _min_filter;
GLenum _mag_filter;
GLenum _wrap_s;
GLenum _wrap_t;
GLfloat _lod_bias;
GLfloat _max_anisotropy;
};
enum { MaxCachedName = 1 << 20 };
TextureParameters *Params(GLuint texture)
{
if (_disable_cache || texture >= MaxCachedName)
return nullptr;
const size_t had = _texture_params.size / sizeof(TextureParameters);
if (texture >= had)
{
size_t want = had != 0 ? had : 256;
while (want <= texture)
want *= 2;
if (grow_buf_reserve_keep(&_texture_params, want * sizeof(TextureParameters)) == nullptr)
return nullptr;
/* (the buffer may have grown past what was asked: all of it marked) */
memset((TextureParameters *)_texture_params.data + had, 0xFF,
_texture_params.size - had * sizeof(TextureParameters));
}
return (TextureParameters *)_texture_params.data + texture;
}
GLuint _array_buffer;
GLuint _element_array_buffer;
GLuint _texture;
@@ -313,7 +355,7 @@ private:
} _scissor;
GLuint _texture_ids[TEXTURE_ID_CACHE_SIZE];
GLuint _texture_cache_size;
std::map<GLuint, TextureParameters> _texture_params;
grow_buf_t _texture_params = GROW_BUF_INIT;
bool _disable_cache;
};
+29 -7
View File
@@ -142,19 +142,41 @@ __forceinline
shadowed);
UsePipelineShader(CurrentShader);
if (CurrentShader->trilinear_alpha != -1)
/* (the uniforms are the program's: those of the record whose program it is) */
PipelineShader *owner = CurrentShader->general != nullptr ? CurrentShader->general : CurrentShader;
if (CurrentShader->trilinear_alpha != -1
&& (!(owner->shadow_set & PIPELINE_SHADOW_TRILINEAR)
|| memcmp(&owner->shadow_trilinear, &ShaderUniforms.trilinear_alpha, sizeof(float)) != 0))
{
glUniform1f(CurrentShader->trilinear_alpha, ShaderUniforms.trilinear_alpha);
owner->shadow_trilinear = ShaderUniforms.trilinear_alpha;
owner->shadow_set |= PIPELINE_SHADOW_TRILINEAR;
}
if (palette)
{
if (gp->tcw.PixelFmt == PixelPal4)
ShaderUniforms.palette_index = gp->tcw.PalSelect << 4;
else
ShaderUniforms.palette_index = (gp->tcw.PalSelect >> 4) << 8;
glUniform1i(CurrentShader->palette_index, ShaderUniforms.palette_index);
if (!(owner->shadow_set & PIPELINE_SHADOW_PALETTE) || owner->shadow_palette != ShaderUniforms.palette_index)
{
glUniform1i(CurrentShader->palette_index, ShaderUniforms.palette_index);
owner->shadow_palette = ShaderUniforms.palette_index;
owner->shadow_set |= PIPELINE_SHADOW_PALETTE;
}
}
if (clipmode == TileClipping::Inside)
glUniform4f(CurrentShader->pp_ClipTest, clip_rect[0], clip_rect[1], clip_rect[0] + clip_rect[2], clip_rect[1] + clip_rect[3]);
{
const float clip[4] = { (float)clip_rect[0], (float)clip_rect[1],
(float)(clip_rect[0] + clip_rect[2]), (float)(clip_rect[1] + clip_rect[3]) };
if (!(owner->shadow_set & PIPELINE_SHADOW_CLIP) || memcmp(owner->shadow_clip, clip, sizeof(clip)) != 0)
{
glUniform4f(CurrentShader->pp_ClipTest, clip[0], clip[1], clip[2], clip[3]);
memcpy(owner->shadow_clip, clip, sizeof(clip));
owner->shadow_set |= PIPELINE_SHADOW_CLIP;
}
}
if (clipmode == TileClipping::Outside)
{
if (ShaderUniforms.base_clipping.enabled)
@@ -186,7 +208,7 @@ __forceinline
&& (gp->tcw.ScanOrder == 0 || gp->tcw.PixelFmt == PixelPal4 || gp->tcw.PixelFmt == PixelPal8);
#ifdef GL_TEXTURE_LOD_BIAS
if (!gl.is_gles && gl.gl_major >= 3 && mipmapped)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, D_Adjust_LoD_Bias[gp->tsp.MipMapD]);
glcache.TexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, D_Adjust_LoD_Bias[gp->tsp.MipMapD]);
#endif
if (gp->tsp.FilterMode == 0 || palette)
{
@@ -197,7 +219,7 @@ __forceinline
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mipmapped ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
if (gl.max_anisotropy > 1.f)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.f);
glcache.TexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.f);
}
else
{
@@ -209,7 +231,7 @@ __forceinline
{
if (settings.rend.AnisotropicFiltering > 1)
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
glcache.TexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
std::min((f32)settings.rend.AnisotropicFiltering, gl.max_anisotropy));
/* Set the recommended minification filter for best results.
* Not for a punch-through polygon: blending two levels
@@ -220,7 +242,7 @@ __forceinline
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
}
else
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.f);
glcache.TexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.f);
}
}
+4 -3
View File
@@ -304,7 +304,7 @@ mediump vec4 palettePixel(highp vec2 coords)
void main()
{
// Clip inside the box
if (pp_ClipInside==-1)
if (pp_ClipInside == 1)
{
if (gl_FragCoord.x >= pp_ClipTest.x && gl_FragCoord.x <= pp_ClipTest.z
&& gl_FragCoord.y >= pp_ClipTest.y && gl_FragCoord.y <= pp_ClipTest.w)
@@ -587,6 +587,7 @@ static void pipeline_locations(PipelineShader *s, bool general)
s->prepass_alpha = glGetUniformLocation(s->program, "prepass_alpha");
s->key_bits = general ? glGetUniformLocation(s->program, "key_bits") : -1;
s->key_now = ~0u;
s->shadow_set = 0;
ShaderUniforms.Set(s);
s->uniforms_set = gl.uniforms_frame;
@@ -1088,9 +1089,9 @@ static void gl_term(void)
if (gl.depth_ubo != 0)
glDeleteBuffers(1, &gl.depth_ubo);
gl.depth_ubo = 0;
glDeleteTextures(1, &fbTextureId);
glcache.DeleteTextures(1, &fbTextureId);
fbTextureId = 0;
glDeleteTextures(1, &fogTextureId);
glcache.DeleteTextures(1, &fogTextureId);
fogTextureId = 0;
glcache.DeleteTextures(1, &paletteTextureId);
paletteTextureId = 0;
+15 -1
View File
@@ -76,6 +76,8 @@ extern xform ViewportMatrix;
void DrawStrips(void);
enum { PIPELINE_SHADOW_TRILINEAR = 1, PIPELINE_SHADOW_PALETTE = 2, PIPELINE_SHADOW_CLIP = 4 };
struct PipelineShader
{
GLuint program;
@@ -91,6 +93,14 @@ struct PipelineShader
GLint palette_index;
GLint shade_scale_factor;
GLint prepass_alpha; // -1 where there is no depth prepass (GLES2): rend/sorter.h
/* What the program's per-polygon uniforms were last set to, a bit in
* shadow_set each (SetGPState()): set again only when another value
* is. A record drawn with its general program uses the general one's;
* a program given its locations has none. */
u32 shadow_set;
float shadow_trilinear;
GLint shadow_palette;
float shadow_clip[4];
//
bool cp_AlphaTest;
@@ -262,7 +272,7 @@ extern struct ShaderUniforms_t
int palette_index;
float shade_scale_factor;
void Set(const PipelineShader* s)
void Set(PipelineShader* s)
{
if (s->shade_scale_factor != -1)
glUniform1f(s->shade_scale_factor, shade_scale_factor);
@@ -291,7 +301,11 @@ extern struct ShaderUniforms_t
glUniformMatrix4fv(s->normal_matrix, 1, GL_FALSE, normal_mat);
if (s->palette_index != -1)
{
glUniform1i(s->palette_index, palette_index);
s->shadow_palette = palette_index;
s->shadow_set |= PIPELINE_SHADOW_PALETTE;
}
}
} ShaderUniforms;
+48
View File
@@ -0,0 +1,48 @@
#!/bin/sh
# Polygons drawn outside a clipping rectangle (a tile clip of the
# "inside" kind: rend/tileclip.h), with OpenGL - each sorting and so each
# renderer (per-pixel: OpenGL 4), on OpenGL and on OpenGL ES 3 - on Mesa's
# surfaceless EGL (gl_frames.c), as tools/gl/gl_dma.sh has it. The test
# disc built with -DLIVE_CLIP (tools/threads/live_disc.py --clip) draws
# three rectangles of one kind, the second's clipping rectangle another:
# each has to show a hole where its rectangle is (tools/rend/clip_check.py).
#
# sh tools/gl/gl_clip.sh [core.so]
#
# KEEP=1: the pictures and logs are kept, in $TMPDIR/gl_clip.<pid>.
set -e
cd "$(dirname "$0")/../.."
CORE=$(readlink -f "${1:-parallel_dc_libretro.so}")
WORK=${TMPDIR:-/tmp}/gl_clip.$$
mkdir -p "$WORK"
[ -n "${KEEP:-}" ] || trap 'rm -rf "$WORK"' EXIT
${CC:-cc} -O1 -g -Wall -o "$WORK/gl_frames" tools/gl/gl_frames.c -lEGL -lGL -ldl
python3 tools/threads/live_disc.py "$WORK/test.gdi" --clip
fails=0
for api in gl gles; do
for sort in per-triangle per-strip per-pixel; do
case $sort in
per-triangle) o="reicast_alpha_sorting=per-triangle (normal)" ;;
per-strip) o="reicast_alpha_sorting=per-strip (fast, least accurate)" ;;
per-pixel) o="reicast_alpha_sorting=per-pixel (accurate)" ;;
esac
name=$api-$sort
echo "$name"
rm -rf "$WORK/dir"; mkdir -p "$WORK/dir"
# (GLF_GLES set at all asks for OpenGL ES)
gles=; [ $api = gles ] && gles=GLF_GLES=1
if ! env $gles GLF_DIR="$WORK/dir" "$WORK/gl_frames" "$CORE" "$WORK/test.gdi" "$WORK/$name.raw" 70 "$o" \
> "$WORK/$name.log" 2>&1; then
echo " FAIL: the $name run failed:"; tail -5 "$WORK/$name.log"
fails=$((fails + 1)); continue
fi
python3 tools/rend/clip_check.py "$WORK/$name.raw" || fails=$((fails + 1))
done
done
if [ $fails != 0 ]; then
echo "gl_clip: $fails failed"
exit 1
fi
echo "gl_clip: all passed"
+48
View File
@@ -0,0 +1,48 @@
"""Judges a picture of the test disc built with -DLIVE_CLIP
(tools/threads/live_disc.py --clip), in vk_hdr's form (a 16-byte header:
"VKHR", the Vulkan format, the width, the height; then RGBA8 rows, top
first; 640x480, the disc's 320x240 doubled). Three rectangles of one kind
are drawn outside a clipping rectangle each, which leaves a hole in each:
cyan (over a yellow one) with its hole at x 64-127, y 256-319, and green
with its hole at x 192-239. The holes have to show the white background
and the rings their colours. A renderer that does not clip "inside" (draw
outside the rectangle) fills the holes; one that keeps the green one's
rectangle for the cyan one fills the cyan one's.
python3 clip_check.py picture.raw"""
import struct
import sys
def main():
try:
data = open(sys.argv[1], 'rb').read()
except OSError as e:
print(' FAIL: no picture: %s' % e)
return 1
if len(data) < 16:
print(' FAIL: not a picture')
return 1
magic, fmt, w, h = struct.unpack('<4I', data[:16])
if magic != 0x52484b56 or fmt != 37 or (w, h) != (640, 480) or len(data) < 16 + w * h * 4:
print(' FAIL: not a 640x480 RGBA8 picture: %x %d %dx%d' % (magic, fmt, w, h))
return 1
def px(x, y):
o = 16 + (y * w + x) * 4
return data[o:o + 3]
checks = [
('the cyan one\'s hole', (96, 288), lambda c: min(c) >= 240),
('the cyan one\'s ring', (52, 244), lambda c: c[0] < 16 and c[1] > 240 and c[2] > 240),
('the cyan one\'s ring, below its hole', (96, 330), lambda c: c[0] < 16 and c[1] > 240 and c[2] > 240),
('the green one\'s hole', (215, 288), lambda c: min(c) >= 240),
('the green one\'s ring', (180, 244), lambda c: c[0] < 16 and c[1] > 240 and c[2] < 16),
]
bad = 0
for what, (x, y), ok in checks:
c = px(x, y)
if not ok(c):
print(' FAIL: %s at %d,%d is %d,%d,%d' % (what, x, y, c[0], c[1], c[2]))
bad += 1
return 1 if bad else 0
if __name__ == '__main__':
sys.exit(main())
+422 -2
View File
@@ -2,7 +2,7 @@
"""Write a bootable GD-ROM image (GDI + three track files) around the
bare-metal test program in live_prog.c, for tools/threads/live.sh.
Usage: live_disc.py out.gdi [--no-region-array | --mmu-align | --slant | --hscale | --cull | --atlas | --dma]
Usage: live_disc.py out.gdi [--no-region-array | --mmu-align | --slant | --hscale | --cull | --atlas | --dma | --clip]
live_disc.py out.elf
The program is carried here already compiled, so the test needs no SH-4
@@ -2925,6 +2925,424 @@ e040018ce0ffff03000000e0111111113c0000ffe0ffc0004000000064000000
""")
# The program built with -DLIVE_CLIP (live_prog.c): with three rectangles
# of one kind each drawn outside a clipping rectangle, the middle one's
# rectangle another, for a renderer that sets its programs' clipping only
# when it changes (tools/gl/gl_clip.sh).
PROGRAM_CLIP = bytes.fromhex("""
01df02d02b40090000f0008c100f018c020005d119206b200e40543402004227
01170b0009000900fefcffff020005d119205b200e4044340200422601160b00
09000900fefcffff1b4429000b0009000344422642605a200224074402000116
42160b00090002000b0009000e440b0009006a446a000b00090066446a004225
0b0009006a000b000900020101e00b000e4100e00b000900034f01e00b00074f
00e00b0009000900000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
000000000000000004d105d002600221420002704e402b00090009004440018c
240000ff00000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
000000000000000016d0026116d229211ad208e522633031128908721045f98b
13d33031068913d3392113d33b210be402a009000dd10ae411d33b2103a00900
215109e45834111004510ed33921184408444b2114100cd22263017332223800
2b000900000000ff00f0ffff00000010000000e000f0ff000000000c0040018c
76010000ff03ffff4040018c220203d12e41fdff2e420b0009000900a800018c
8df09df19df29df39df49df59df69df7830410f020f030f040f050f060f070f0
3df05a000b0009005a440df24df21df25a000b00090048f44cf55af50b000900
48f00b0009000af40b0009000dd10ed2222104710dd2222104710dd222210471
0cd2222101e10cd212220cd200e3322204721222f0720ad33222147212220b00
0900090000e000ac0100008000e1000c0920000100000001e86c5fa0106c5fa0
00e0000c43631843436128411b234b23136418444b2304d210e132221041fc8f
04720b0009000900000000a409d112610f92292108d307d42365336242661367
636159217231028b10431042f68b0b000900ff030c815fa080841e0002d11260
01910b001920ff030c815fa0862f962f224f436804d90b4909008230fb8b264f
f6690b00f66809003c06018cf47ff361319221214d6229444c602d9323673927
7b2011812c60128100e013815d6229455c6029233b2014812c6015810fd101e2
20211871a0e220210dd20ed1206008c8018f1041fa8b0ad1106008c80989f362
09d706e1256331271041fb8f01e00b000c7ffcaf00e0302000ff090084705fa0
18705fa0a086010080705fa027d100e220210c71602163621942047120210871
a0e2202122d223d1206008c8018f1041fa8b1fd1106008c8348d06e11ed34562
21231041fb8b1ad21ad1206008c8018f1041fa8b16d1106008c8258d68261d8d
536400e014d70174180063610a3101416362017202e33232078d017105a001e1
194224041041098d027071622d62036301736233f48f2405f5af104107d11061
0b0001e00b0000e00b0000e084705fa018705fa0a086010080705fa09c705fa0
862f962fa62fb62f224f907f43680ae6f36572d472d00b400c750820018bbba0
09e0fc840fc903886c8f01e0f3600c7002840188668f01e0f3600c7003840288
608f01e0f3620c7225840c63284326840c6118613b2127840c600b21b9931c33
b8922633018b9ea001e08828488d83605cd222621032018b97a002e00ee6f365
59d457d00b400c750820018b8fa009e0f3600c700184118801898aa003e0f360
0c7004840188018985a003e0f3600c7005840288018980a003e0f3620c722b84
0c6328432c840c6118613b212d840c622b217e922c317d922631018b6fa003e0
64e6f36541d43ed00b400c750820688df365f36a107af36800eb6c9414a00ce9
38d2122248a0090010420a8d0177346040c8f98d7b6043610c416f841b20f3af
6f801049088d087ab0280178a36300e78366f076ebaf08e200e12dd64c940ca0
0ae01c311042068dff736821f9891c314a21f7af1d611040068954622c621842
2a217363f1af7362f06001882b8bf36001840288298b1fd22a21f362fa840c62
136319433032228ff3622b840c621c611032ffe00a600840707f264ff66bf66a
f6690b00f668f7af0900f5af0900f3af0900f1af0900efaf0900edaf0900ebaf
0900e9af09e0e7af04e0e5af04e0e3af04e0a8fd2b018000211009008c32018c
ec06018c1480018c8032018c7432018c00800000ffff00000ad1422104715221
01e109d21222087212220472122207d1226001c8018d1041fa8b05d1126001c8
0b00290004745fa00c745fa000093d0018745fa0626113671947014725922821
ffe22a62436300432903136001402444136019407a2001c8108943605c302a23
063429073a2773611841236318433c333b21203729022b210b00122643605830
2a2346302907eeaf3a27000249d1126249d11032048d00e248d122210b0000e0
47d148d2122248d221521032718b47d112600188028d11e00b00090044d345d1
3111047144d2292144d645d772166c921b222316726230325d8f00e341d23222
736301e77223226360922033558b39d2215337d22033df8f13e03bd23bd33112
3bd334d733173bd773123bd226164e921b22271639d222623032cd8f16e038d2
2dd3392243932b23371624d305e7722332d332672fd33037318b32d32fd73227
29d7735730372c8b22d3335728d33037b28f18e02cd778162a977b2119162bd1
01e332212ad1126321d11033a48f19e025d338167b22291624d102e2222124d1
126220d110320e8f00e220d122210b0001e00b0011e00b0012e00b0013e00b00
17e00b0018e00b001ae01601c0003601340109000000001044332211100000ff
88776655040000100050018c4040018c5555aaaafc5f018c00f0ff1f0040018c
001000104440018cfc6f018c0df0ad0bb0b0b0b0c0c0c0c00030001008300010
0070018cc1c1c1c100400010000000ff08400010862f962ff45110d2222210d8
422810d0522010d4122410d5622510d6722610d7122710d3f25992238302f359
9222122800e11220122412251226122712238302f6690b00f6680900000000e0
040000e0080000e00c0000e0100000e0140000e0180000e01c0000e0862f962f
a62fb62fc62fd62fe62f224ff07f536b636a7369fd5e2ad11b24431f29dd422d
00e829d1822129d282220871822108728222087182210872822208718221830d
e21f611f722f22dcfc575366a3650b4cb364e21ffc52211f922f2367b366a365
0b4c93641bdee21fa11f922ffc57b366a3650b4cb364f351122d17d112d21222
12d182210872822208718221087282220871822108728222830de21ffc51111f
922f1367b366a3650b4c9364107f264ff66ef66df66cf66bf66af6690b00f668
00000081000000e0040000e0080000e0b40b018c0000803e00000024224ff87f
f351111f722f6367536643650cd10b4100e40cd200e112220bd3122304731223
04731223047312230473122304731223047312238302087f264f0b0009000900
1c0c018c000000e0040000e0862f962fa62fb62fc62fd62fe62ff75af85ef95d
fa5cfb5b26d227d1122227d900e1122926d8122826d0122026d3422326d45224
26d5622526d67226830226d7722226d7722926d7722812201223122412251226
83022222e229d22821d7722012231224a225122683022222e229b22872201223
1224a225122683022222c229d228722012231224a2251226830216ddd222c229
b228722012231224a22512268302f66ef66df66cf66bf66af6690b00f6680900
000000e000000020040000e0080000e00c0000e0100000e0140000e0180000e0
1c0000e000000380000000c0000080200000003f000000f0862f962fa62fb62f
c62fd62fe62ff75cf85bf95e24d14b2124d2122224da25d1122a25d9522925d8
622825d000e1122024d5122524d6122624d312238302436d2b2dd222b22ae229
c22812201225722612238302d222b22afb5bb229c22812201bdbb22572261223
8302d222fa5dd22ae229c228b220122572261223830215dddb244222fa54422a
fb5dd229c228b220b225722612238302f66ef66df66cf66bf66af6690b00f668
00000080000000e0040000e0000000c0080000e00c0000e0100000e0140000e0
180000e01c0000e00000803f000000f0862f962fa62fb62fc62fd62fe62f224f
bc7fc07f9bd112619bd22665266622679ad2266322625b216b217b213b212b21
44e0160f96d11060fec90021ffe295d322230473222300e204732123106001cb
002191d1126091d3392005cb022118718fd33221f471222110718ed222216c71
8dd222219c718dd2222104718cd2222110718cd222218cd18cd212228cd21222
8cd18dd222218dd600e38dd88dd412e745330a02094223611c3113652c355c35
3369583953609c3008408e01122601731047ed8f047684d184d2222184d185d2
2221047184d22221047184d2222184d1c59222210471c392222104711fe22221
0471be9222217fd27fd310e132221041fc8f04727dd1b59222213c717cd22221
0871077222217bd27bd3ac9132221041fc8f047279d17ad77ad310e27221a396
1c3632261042f98f047177d177d2222104719a92222176d276d310e132221041
fc8f04721fe174d2122274d2122274d100e002ea00e973d807e41fe586978693
17e6d62f019d230df66d14211043058d017726657035f989f8af63616ad22753
6ad22033648d48e001e1160f10e168d212220472122267d267d30ce132221041
fc8f047200e163d212220472122263d163d2222163d164d2222164d12e4164d1
ffe700e308e2722131111042fb8f08715fd360d1122360d260d1292148977b21
11135fd1122200e111125ed212225ed212225ed105e222215dd00b40090040e7
fc3700175bd1eee2339320215ad71c3720275ad71c37202759d71c3720271043
f38f017157d458d00b4005e5082001ee028de61fb4a0090054d00b40090054d4
54d10b41092454dcc36dc51fbda200ee52d2205335d22033978996af01e1007c
e003ff7f8001800000101f7ce00700f836010048846c5fa0f46c5fa0c0785fa0
0400d8ff0800d8ff44805fa0000080003f7d1700000020000401150000007f02
0000df0177df27000000100020805fa028815fa08c805fa0000000030c0010a5
0433018cabaaaaaa88805fa017b7d138000010a5000000020000802000000030
04905fa0001000a40303030374805fa000ff000000825fa080800000002006a4
050505050606060614905fa01ffc0000000006a4070707071c985fa01c9c5fa0
004006a4ffff0000c040018c11111111380000ff000018a500000080180018a5
4c0e00a02c805fa000001800a800018c0040018c000000100050018c00f0ff1f
443322114040018c000000ff100000ff2c0a018c000023ac0000ffff0000feff
0000fdff5eb000006c06018c6600018cff7fffff6c00018ccc32018c0041018c
bd9562d462d00b4009000820028fe61f42af09005fd460d00b4005e50820018b
a3a002ee5dd8ab9b59da5dd9b3650b4a83640820018b9aa002eea1900c389038
f58fb36553d454d00b4008e50820018b8fa003ee959553d44dd00b4009000820
028f10e187a003ee4fd650d220677c6764633c633037028d10417ea004eef58f
01724bd17b9210677c674ad31c3330633c633037028d1042a8a005eef38f0171
45d16c9210677c6744d31c3330633c633037028d10429ba006eef38f017135d4
35d00b4002e50820018b93a008ee3cd13cd2222104715592222101e13ad21222
087212220472122238d1226001c8018d1041fa8b36d1126001c8028ffc717ba0
09ee00e222210471126001c8018975a00aee30d1379202a009000c8d01711067
7c672dd31c3330633c633037f58d10420beeb1aee61f29d110611c6124922031
5e8f0cee26d110611c6120315a8f07ee24d110611c612031568b23d110611c61
2031538b00ee97aee61f95aee61f93aee61f91aee61f8faee61f8daee61f0028
0004004000180010ee0009000000200c9809018c5eb000006c06018c0000210c
0028210c0000220cf432018c000020ac000021ac0000ffff000022ac0000feff
04745fa00000230c0c745fa0801a060018745fa0000023ac0000fdff001023ac
002820ac002821ac004022ac54aee61f52aee61f50aee61f4eaee61f4caee61f
4aaee61f48aee61f46aee61f44aee61f017993600888038f936800a1017e9368
07e00d4846911928936001c90b2870e0860fd26af553326bf3667076b3651ed7
0b47a364071ff36778778366b3651bd00b40a36440e2fc322e51f7501030318f
70e02f5127922921fe022031028de82ed1a001eecc8ff366860f7076a3650ed7
0b47a364036bf366787683650cd00b40a36440e2fc322e51103b0189bda002ee
2f51089339212c522031b28d017902ee0aa0e51f00030103d409018c1000018c
2c00018c01eee51f59d10b4100e859d00b400900036158d956db0b4b136aa230
058f0361a030ffe22a381049f58b63e1163801ee028fef1f07a209004fd850d1
122850d10b41090082624fd34ad1326001c8018d1041fa8b4bd11261136001c9
081f136001c8018910a202e043d1126343d11033018b0ba203e040d1126040d1
1032038dff88018904a201e03bd11260ff885e893ad222213cd13dd222210471
3cd222210471629222213bd13bd22221147101e2222113622fd1226001c8018d
1041fa8b2dd112611c6113600688018909a204e029d22260294019403088028d
747202a204e022632dd220330189fea105e022d222d3322224d22ad332220472
24d33222047228d33222047227d33222047227d3322227d727d320e232270173
1042fb8f04771cd21cd33222147201e33222236310d2326001c8018d1042fa8b
0ed22260ff88048922600c6007880089181fa0a1090057afe51f55afe51ff550
0470051fe3600a882889f8ae00e902200c06018c3c06018c80841e0000e100ac
785634128c05018c186c5fa000e000ac0000008000e1000c046c5fa000e0000c
56657273220000800c010022000000020000006414e000ac0000dec0047d73d1
103d018912adc51f71d8836c057cf36a547a70db84600c69ab80f3640b4b5f74
036168e0982929022031028d160f19a103eeab840c61c8909b201030028dc038
12a103eee68b64d8836e147e63dd64dc64db65da0b4d0900051f8669f3667876
9365f3640b4c747440e3fc333e52f361707110320189f9a004eef3617471a490
fe0210320189f3a004ee3c52f5512a219a21b9213f533a22a9222b211821028d
e038e7a004eed58b50d00b40f36b036a4fd9936e187e4fdd6c7b4fdc96680b4d
83640038028d02e0d6a005ee0a2868e0860fb365f3640b4c68740038028d40e2
cca006eefc322b51b031028d903ec7a006eee38b41d1181241d1191241d93dd8
0b48936440dbf3640b4b60743fd40b4807eef3640b4b64740b489364f3643cd1
0b41687440e7fc377a5278511032478fe51f36d11a17f3656c75f3642ed00b40
6874f36432d10b41687440e1fc311a521951103208ee338fe51f2ed00b4009ee
01882d8fe51f2cd00b400aee0188278fe51f2ad483600b400bee20d428d00b40
090028d110301b8fe51f27d424d00b4009000188148fe51f24d140e0fc301810
00e11a10f3656875f36421d10b41607440e3fc333a52385110320cee028de51f
42ae0900a36435a083608000f432018cc432018c4800018cb032018c6600018c
5000018cf2830070f38300708400018c9832018c7200018c7a00018c0000803f
00000040010004008005018c010024008605018c8a00018c9800018c01000c00
6805018c000080bf01000080785634127605018c0b40090044d800e1122844d1
0b41090082627e9110320dee088fe51ff36440d00b4068740788138f00eee51f
f2ad0900f0ade51feeade51fecade51feaade51fe8ade51fe6ade51fe4ade51f
e2ade51f0eeedfade51f33d9926833d10b41090064e1122931d120e222211362
30d1226020c8018f1041fa8b2ed00b40090029d182212ad120e2222103619c71
02e2263129011c31d8ad1f1f03a0081f01a0081f081f25d1126425d125d22363
136600e020e73365283540755026017600231047f78f01731cd31fd77223dc73
00e772231dd7122704772227047710e662271997722323651075136000eee71f
01e808a010e3d6af081fd4af081fd2af081f1043248924666c6604677c677036
f789f6af871f8001115409004440018c2c05018c4005018cc8805fa00c06018c
00695fa080841e003c06018c4000a0ff9040018c7040018c018200001000a0ff
6ed22062282203890192230209005e166bd2226210711032228df75ee82e018f
03eee71f67d268d112220171111267d100e332213111321133113411351164d7
32275fd32223136504731223047304e66223a893322710715bd004e813a004e3
5cd112615031d98ff75e5bd112611821d48ff75e56d1126002c8d38fe82eceaf
090010430789f75ee82efa8f56660036f789f6af871f4dd334533823048ff75e
47d3326330322589e82e2a8d05ee00e247d122212471422148d149d33b2149d3
222349d249d72227326320331b8d00e244d3222341d3222345d2222132632033
01ee248fea1f126241d110321b8ff3611fa0787137d222621032d58ff75ed7af
00e2d4afe71f726337d22033ffe22a622a1f00e233d3222330d3222334d22221
32632033df89fa5eeb6102711a1ff3617871399221213890118158e012810fe0
138133901481329015812ad100e220211871a0e2202128d228d1206008c8018f
1041fa8b24d1106008c81a8d09eef362787223d306e1256771231041fb8b21d1
0fe2222120d1139222210471f072222100e11ed30d940d950d961dd80c994aa0
00e2ecafe61f314420010002000383001f0fff0077772c01a10f09008040018c
1000a0ff6840018c0100feca5040018c1c00a0ff1400a0ff1800a0ff4c40018c
000000e0000014a50df0ad0b000014e52143341284705fa018705fa0a0860100
80705fa0002870a0402070a0000081a04b1714060a071360783001400c37f5e0
0d4797071a0013670837d59eec3771230172027361714632ec8f853151230172
02736032f78f617100e16cd21222047212220472c193322204721fe332220472
bc93322204721222047212220472122263d1b4922221047160e2222161d100e0
ae9500e6ad97ad935fd25630d62f019d230df66d2814612101707030f58f0271
00e15ad212220472122204729993322204721fe3322204729093322204721222
047212220472122204728c933222047248e3322288924ed32223047322230473
222304732223047322234ad212220472122249d17992222148d600e7769048d4
7598759373620922236106e55d412c311c312c3145310a021365283501455c32
1942094287021a022831629eec31112601771043e68f027600e13ad212220472
122204724d93322204721fe3322204724493322204724d933222047212220472
122231d13b922221047160e22221d87141922221f65007e11d401fcb40e2fc32
061200eeeb1fee1fed1fffeeec1f00eee91f4ea700e8188d5a884d8f648823d1
2a922221019023008360b01220d10b4102e4ffa109001ed10b4103e4faa10900
1bd10b4104e4f5a109001ad115922221019023008360841230f800011f3c1f0f
ff07001070170f0f00190242ff0fcf07011019fc00780342e003e07f040070a0
240070a0000082a090e80000840070a0ac0070a0c00070a0800070a0000083a0
6b6f4606040170a0240170a01c985fa0e405018c10905fa002890c911038138b
06d107d2222183606488178b05d10392222113a00900c8001f7c0900000070a0
01c200001c9c5fa0e79110380389019023008360d01173d173d2222173d174dc
122c74d1126340e2fc32331272dee2644412c265551201e2222120e1122e6fd1
122c6fd16fd612266fd712276fd170d0122070d1141f131f00e9921f911f6ed2
222f6eddba9700e66dd50b4d00e46ddbb41fb31f921f911f6bd1122f6bd700e6
67d50b4d00e46ada922a6ad392236ad492246ad592256ad692266ad792276ad0
92206ad19221830affe369d2322210e1122e68d1122c68d152d4122452d51225
52d753d67226b41fb31f921f911f52dee22f62d700e652d50b4d00e4922a55d0
922055d1922155d2922255d3922355d4922455d5922555d69226830affee54d7
e22757d112611821018b5ba101e255d1126177e22631028f01717aa1090051d2
122251d112600820018bd9a1090009e11630018b1ca20a8801704bd102212dd1
4ce0fe044221047150e0fe055221147154e0fe066221087144d22221047144d2
222144d111600d6043d111611f88028f1d6114a2090041d641d240e1236740e3
61271043fc8f027729901041f68f0c322691163829023bd1088f222119d13ad2
22211e911038018b38d1106118d117d2222134d112611821018902a2ffe718de
33dcc41f13d1131f32dbb21f32ddd11f11daa22f11d961a0ffe7a00000800001
03010601000070a0018200000000200060805fa048805fa04c805fa000007001
00007f0068805fa06c805fa00000008044815fa0000000430000003f380e018c
00008020000080429a99193fff0000ff000000e0040000e0080000e00c0000e0
100000e0140000e0180000e01c0000e014805fa00000740100003f0000ff00ff
2480018c2080018c1880018c00007f020000df01140a70a4c80a70a400f80000
804070a42880018c0000c700010071a00000dc4200008c420000004200e6c294
0b49e365c41f61d1131fb21f60d1111fa22f60d760d5b6940b4900e6c41f5fd1
131fb21f5ed1111fa22fffe75dd6ab940b49e3655cd1141f5cd1131f5cd1121f
d11fa22fffe700e65ad50b4900e45ad1141f5ad1131f5ad1121f5ad1111fa22f
ffe759d659d50b4908e459d159d2222159d25ad332220472e222047200e33222
047257d33222047232220472322204723222830100e048dd52dc13634edb52da
52d953d400e253d563a104e11638018b53a7836050d151d2222151d1ffe22221
50d10b41090050d110600fc90688018bf0a409004dd1126118211e894cd11261
18211a8f0820028d09e1b3a501e240eefc3e1e1e1f1e47d1126004e700e600e5
f3640b407874ee511821028d01e242d1222142d2226101711222d0a4836040d1
2dae09003fd122213fd2222c32d133d22221b41fb31f921f911f2cd1122f3bd7
00e63bd50b4d00e4922a23d29222047292220472922204729222047292220472
922204729222830a25d1ffe2222188ae090030d111611d612fd00031f7892fd1
01e27eae22218000880009000000304300001043ffffff800000908000009043
000040430002003000005c420000c0420000f0410000002000002f4300009643
0000fa4200007a4300000e0c24008020000000e020000080040000e0000000c0
0000803f000043430c0000e00000003f100000e0180000e044815fa000000080
14805fa08c05018c8c705fa01080018c0880018cbc00008c0080018c0c80018c
000020002480018c00007801ff00ffff00008020280a78a41ff800001c80018c
54daa26c54d1cb21122a54d110e22221147153d2222153d153d2122204721222
52d153d2222153de53dbb41f53d1131f00e9921f911f52d1122f52dd869700e6
e3650b4d00e4b41fb31f921f911f4ed1122f4ed700e6e3650b4d00e44cd19221
4cd29222047292220472922204729222047292220472922204729222830146d1
ffe22221c22a45d101e22221e7ad0900fc8b43d111600d60407111611f88078d
1d6140d101e222213cd10be2d7ad22214d922c313cd33821f689f2af09004697
7c3139dee821068b36d112611821058936d6e1ad090035d6dead09003896dbad
090033d1141f33d1131f33d1121f33d1111f33d1122f00e61ed522d10b4100e4
edad090029071ceeed473b27722340e7fc377357722b2bd76227922a22240c77
222729dee2250877222783031041518d017002c840e6fc36d3160089c31601c8
22d6df8d038822d6ddaf290700802004e0070900f4805fa0000001004c805fa0
00007c0100003f0068805fa044815fa000000080000080200000804200000043
0000003f380e018c9a99193fff0000ff000000e0040000e014805fa01880018c
200a7ca41c80018cdfff000000f800000000484300809f430000704200005243
6666663f080000e00000803f0000574300006b4385daa41f85d1131f85d9921f
85d1111f85dee22fffe785d685d586d00b4008e4a41f85d1131f921f84d1111f
e22fffe783d684d57fd10b4108e483da83d4422a83dd84d2222d84db84d1122b
84dc85d6622c00e984d3922384d5922584d7922784d09220830ab22a83d1122d
83d7722be22c92239225ffe2087322239220830ab22a122d7ed3322be22cfc75
92257dd1f87012200875222508709220830ab22a79d0022d722be22cf8751225
047592250475222504759225830a74d5522a022d322be22c68d0122004701220
0470222004709220830a422a5ed4422d6cd4422b622c61d69226f870922061d4
92240c769226830ab22a67d6622d722be22cfc709220fc749224087022200874
9224830ab22a622d322be22c53d69226fc701220fc7422240c769226830ab22a
5ad6622d722be22c4cd712279220222408709220830a522a622d322be22c1227
47d3122322249220830a51d2241f131f621f50d1111fe22fffe737d5559432de
0b4e00e64cd1122a37d0022d32d1122b4ad1122c30d339d232222ed538d45224
38d6922638d79227830a00e300e2c36b636affe9736421a004e5236740d141dc
236641dddb2725dd722d26de022e27d0c2201ad7722b29dc122c29d76227922a
2224830d122d622e9220222b222c2227222a2224830d10456c8d017302e13360
02c8028d3921d0a401c801c801898da403882ed01821d08f13672adcd1af1366
2ad027d12adcccaf1366800000006b430000f04100005743000020410000003f
00c0003400208020380e018c00005c4200000c4200c0003600008020000000e0
08000080040000e0000000c0080000e0002480200c0000e000c80088100000e0
140000e0180000e01c0000e00000f0420000e041000050420000004300002043
080000f00004802000002443000040430000604300008042c800008000020030
0000803f00005643c80000e00000484300006c437fdaa41f7fdcc31f7fdee21f
7fdbb11ff391122f7ed904e701e604e50b4901e4a41fe31fe21f7bd1111f7bd1
122f04e703e604e50b4903e4a41fc31fe21fb11f76d0022f04e701e604e50b49
01e4d36a00e9922d047d922d71dc922c71d1922171d2922271d3922371d49224
71d59225830a71d571dbb11f71d1122f71de72d753660b4e00e4b11f70d1122f
70d771d671d50b4e00e471d1122a922d922c61d6922602e260d7222706e160d0
122060d3222360d41224830ab11f67d5522f68d768d669d569d40b4e0900922a
922d922c54d6922654d7922754de922e54d0922054d19221830a62d2241f62d1
131f62d3321f62d4411f62dee22f62db62d663d563d40b4bffe75ad5541f62d1
131f5ad6621f61d1111fe22f60d65cd55cd40b4bffe7922a922d922c3ed79227
3ed092203ed192213ed292223ed39223830a58d4441f4ed5531f57d6621f57d7
711f57d0022f57d757d558d40b4b00e650d1141f56d2231f4fd3321f27d4411f
54d5522f54d750d550d40b4b00e6922a922d922c28d6922628d7922728d09220
28d1922128d29222830a4cd14cd222214cd1141f4cd1131f4cd1121f4cd1111f
4cd1122f1ad700e64bd50b4b00e426d3341f4ad1131f3ed4421f49d1111fe22f
48d749d514940b4b00e6922a922d922c11d5922511d6922611d7922711de922e
11d09220830a41d141de83a0122f00ff8000090000002843000090420000f042
0000c0414c0d018c0000b04200ff00ffffff00ff080000e00c0000e0100000e0
140000e0180000e01c0000e00000a0420000403f0000c8421c0c018c00007043
00006f43000074430000524300005c4300000020000000430000444300008042
0000020000005c42000068430000f041000048430000003f380e018c00c40030
000090200800000400008d4300007a4300c6003000002a430000024300000c43
9a99193fff0000800000909400000002000034439a99993e0000ff8060815fa0
000000800000d84200008c420000be42000020423333333f0000802000009643
00008043ffffff80000090806666663ffc0c018c71d772d672d573d40b4e0900
72d273d773d674d574d40b4e222f74d3341f74d4431f74d5521f74d6611f74d7
722f74d774d575d40b4b00e66cdee41f6dd0031f021f72d1111f72d2222f72d7
6dd56ed40b4b00e6922a922d922c6fd392236fd492246fd592256fd692266fd7
9227830a6ed1ffe22221a4911038018936ab09006bd26cd1226001c8018d1041
fa8b6ad16ad222216ad16bd2222104716ad2222104716ad22221047157d22221
047168d2222168d368d220e1222301721041fb8f047366d166d22221147101e2
2221136258d1226001c8018d1041fa8b09ab090060d222610171122283603c88
028f428808a10900048ffb5ee82e018b07a1090059d10b41090059d10b4110e4
58d1126283639c7356911633028ff95105a109002831fc5e163e008b1c1ffd5e
123e00891d1ffe5e1c3eee1f291f00e148e0fe022822038d1821018961a10900
028d3fe151a1fd521638018b01a18360017883603c88028f3ce1c1a809001638
018ba8a846881e88028f2d88aea80900028f0f88afa809000189f3a183603ad1
0b4101e4836208422c3208428c32184209428c321191192283603fc903610841
0c31184131d33c3131d321211043fc8f027195aa090096009500ff7f0000d442
000082420000c242000034426666663f00004d43000091430000394300008743
00005243000048430000344300000c439a99193fff0000800000909400000002
0000f0429a99993e0000ff800c0000e0100000e0140000e0180000e01c0000e0
14805fa0186c5fa080841e0000e100ac7856341200e000ac2200008000e1000c
0c0100220000006514e000ac0001dec0046c5fa000e0000c1080018c0c06018c
4c06018c0c00d8ffe405018c000000a500a0000083d1222100e183d210220c72
0ae330220472102280d1a0e2202180d280d1206008c8018f1041fa8b7cd11060
08c8238d13e26871212100e221210ae3312121212121212175d276d1206008c8
018f1041fa8b72d1106008c811896871116211601162116211616cd220620fc9
18401c610b216cd2f8ae12226bd1faaf09006ad1f7af090069d00b4000e404a0
0b1f67d00b4001e40b1f66d10b41090065d10b4110e465d1126208af291faa91
10381f8ff55ee82e1c8961d1a493322160d1ec31291f60d2122260d112611821
078d836022625ed11032038f01c8d7a00ae201c8018bf6a009005ad15ad21222
f7ae017887911038d78fce71fd53fc5ee8331633268dfe5e54d3ec337d911633
268d44e0fe000820278df65e48d175933221291ffe5e4dd2ec326e9116325e8d
44e0fe0008205d8ff65ee82e5d8f48e0fe0008205c8fff5ee82e278df85044d1
b9afec313ad15a93322142d1b3af291f37d155933221ddaf291fe82e0e8d58e0
33d1fe000221291ffd52fc5ee8324491163238d19f8dfe5ecdaf0900291f97ae
01e12bd13d922221efaffd520820038df75e31d18faf0c31e82e02892fd18aaf
ec31fa5ee82e02892dd184afec31fb5ee82e038d40e02bd17dafec3101e1fe02
1732038f40e028d175af2c31fe00018802891bd16faf090024d16caf090024d1
69af090023d166af090023d163af090022d160af0900fa00e003c8001f7c007c
ff03007e0880018c84705fa09c705fa018705fa0a08601000480018cffff0000
a007018c0c06018c4c06018c0c00d8ff10905fa00001d0ba4840018c1080018c
0d600d600000400050805fa043c721fe0008d0ba0200d0ba0009d0ba000ad0ba
000bd0ba000dd0ba0007d0ba4d4d0d600300d0ba0400d0ba0500d0ba0600d0ba
5bd11261c971263112895ad112611821138959d11262a9911032028957d108a0
090057d1126118210a8956d101a0090055d156d212220caf836055d1f9af0900
54d1f6af090054d108af090029071ce61821028d6d4773ab090050d050dc51d6
3fab090003c903880189afa83fe13fe11638028d836006ae836201c8018bdea8
090049d1019c230c090014ee47d222621071203102ee068fe71f45d112615031
02ee048de71f01922302090098e9019123010900d6e93360038829071ce11d47
3cd038d136dc0cab1366f48f182139d031ab09004121017002715630058f20c8
d62f019d230df66dceebf38f2364f1af336401a093628362212101700271a630
f78f0388f78d46302c8f66302c8b312101705888f38f027127d12892222127d2
27d1292127d33b2113662367e07726d518e352271043fc8f047724d30ce77223
fc7310e7722322d708e3722101771043fb8f047183061ed700e101e601952305
08e390dec8af5362c6af7362280680000c80018c0880018c0480018c030cd0ba
0080018c040cd0ba010cd0ba4840018c020cd0ba9d5a0d600000200000004843
00006c430000803f000040001000a0ff1400a0ff000070431c815fa0e040018c
e0ffff03000000e0111111113c0000ffe0ffc000400000006400000000000000
400100000e00000000000000100000000a000000000000000100040000000400
0300040001000c00010024000100040001000000020000000001000000020000
0303000000017f80ff000000000000000100000002000000ffffff7f00000080
01000080ffffffff78563412efbeadde000001005345474120534547414b4154
414e4120000000000000000017b7d1380000000000000000ffffffff00002044
0000000017b7d1380000803f00000000ffffffff000000000000f04317b7d138
000000000000803fffffffff
""")
# The same program built with -DLIVE_MMU_ALIGN: its MMU test also asks for
# the address errors a misaligned access raises (live_prog.c). For a core
# that keeps translations in a table, which they all do: it is the build
@@ -3483,7 +3901,7 @@ def main():
if len(sys.argv) == 2 and sys.argv[1].endswith('.elf'):
write_elf(sys.argv[1])
return 0
if len(sys.argv) not in (2, 3) or sys.argv[2:] not in ([], ['--no-region-array'], ['--mmu-align'], ['--slant'], ['--hscale'], ['--cull'], ['--atlas'], ['--dma']):
if len(sys.argv) not in (2, 3) or sys.argv[2:] not in ([], ['--no-region-array'], ['--mmu-align'], ['--slant'], ['--hscale'], ['--cull'], ['--atlas'], ['--dma'], ['--clip']):
sys.stderr.write(__doc__)
return 2
out = sys.argv[1]
@@ -3502,6 +3920,8 @@ def main():
program = PROGRAM_ATLAS
elif sys.argv[2:] == ['--dma']:
program = PROGRAM_DMA
elif sys.argv[2:] == ['--clip']:
program = PROGRAM_CLIP
d = os.path.dirname(out) or '.'
ip = bytearray(16 * 2048)
+33 -2
View File
@@ -28,8 +28,8 @@
* -DNO_REGION_ARRAY for PROGRAM_NO_REGION_ARRAY, with -DLIVE_MMU_ALIGN
* for PROGRAM_MMU_ALIGN, with -DLIVE_SLANT for PROGRAM_SLANT, and with
* -DLIVE_SLANT -DLIVE_HSCALE for PROGRAM_HSCALE, with -DLIVE_CULL for
* PROGRAM_CULL, with -DLIVE_ATLAS for PROGRAM_ATLAS, and with -DLIVE_DMA
* for PROGRAM_DMA. */
* PROGRAM_CULL, with -DLIVE_ATLAS for PROGRAM_ATLAS, with -DLIVE_DMA
* for PROGRAM_DMA, and with -DLIVE_CLIP for PROGRAM_CLIP. */
typedef unsigned int u32;
typedef unsigned short u16;
#define PVR(off) (*(volatile u32 *)(0xA05F8000 + (off)))
@@ -406,6 +406,34 @@ static void ta_quad(u32 pcw, u32 tsp, u32 tcw, u32 white, u32 z, u32 x0, u32 y0,
ta_send(TA_VERTEX | pcw | TA_LAST, x1, y1, z, F(1.0f), F(1.0f), white, 0);
}
#ifdef LIVE_CLIP
/* An untextured rectangle drawn only outside the clipping rectangle (user
* clip "outside": PCW bits 16-17 = 3) of tiles @tx0..@tx1, @ty0..@ty1 */
static void ta_quad_outside(u32 tx0, u32 ty0, u32 tx1, u32 ty1, u32 colour, u32 x0, u32 y0, u32 x1, u32 y1)
{
ta_send(1u << 29, 0, 0, 0, tx0, ty0, tx1, ty1); /* the clipping rectangle */
ta_send(TA_POLYGON | (3u << 16), ISP_GEQUAL, TSP_PLAIN, 0, 0, 0, 0, 0);
ta_send(TA_VERTEX, x0, y0, F(0.5f), 0, 0, colour, 0);
ta_send(TA_VERTEX, x0, y1, F(0.5f), 0, 0, colour, 0);
ta_send(TA_VERTEX, x1, y0, F(0.5f), 0, 0, colour, 0);
ta_send(TA_VERTEX | TA_LAST, x1, y1, F(0.5f), 0, 0, colour, 0);
}
/* With -DLIVE_CLIP, on the left: three rectangles of one kind, each
* drawn outside a clipping rectangle, which leaves a hole in each. Yellow
* with its hole at tile 1, 4 (x 32-63, y 128-159); green with its hole at
* tile 3, 4 (x 96-127); and cyan over the yellow one with the yellow
* one's hole again. A renderer that kept the green one's rectangle for the
* cyan one (one program, its clipping a uniform it sets only when it
* changes) fills the yellow one's hole. */
static void ta_clipped(void)
{
ta_quad_outside(1, 4, 1, 4, 0xFFFFFF00, F(24.0f), F(120.0f), F(72.0f), F(168.0f));
ta_quad_outside(3, 4, 3, 4, 0xFF00FF00, F(88.0f), F(120.0f), F(120.0f), F(168.0f));
ta_quad_outside(1, 4, 1, 4, 0xFF00FFFF, F(24.0f), F(120.0f), F(72.0f), F(168.0f));
}
#endif
/* A screen-aligned rectangle with two textures: the first is what it is
* drawn with, the second what it is drawn with inside a modifier volume
* ("two volumes"). Its parameter has a second pair of texture words, and
@@ -784,6 +812,9 @@ static void ta_scene(void)
* under the two volumes below, which differ only in their cull modes */
ta_quad(TA_SHADOW, TSP_PLAIN, 0, 0xFFFFFFFF, F(0.5f), F(8.0f), F(150.0f), F(40.0f), F(170.0f));
ta_quad(TA_SHADOW, TSP_PLAIN, 0, 0xFFFFFFFF, F(0.5f), F(8.0f), F(176.0f), F(40.0f), F(196.0f));
#endif
#ifdef LIVE_CLIP
ta_clipped();
#endif
ta_send(0, 0, 0, 0, 0, 0, 0, 0); /* end of the opaque list */