KEMBAR78
Ogdc 2013 lets remake the wheel | PPTX
Nguyễn Trung Hưng
Firebat Game Studio - VNG
“DON’T RE-INVENT THE WHEEL”
Our old “wheel”: 3D vertex shader
uniform mat4 uVPMatrix;
attribute vec4 aPos;
void main()
{
gl_Position = uVPMatrix * aPos;
}
The old “wheel” was designed for
But we only need
Remake the old “wheel” for 2D
attribute vec2 aPos;
void main()
{
gl_Position = vec4( aPos.x/HALF_WIDTH - 1.0,
1.0 - aPos.y/HALF_HEIGHT,
0.0,
1.0);
}
• Multiply ops: 16
• Add ops: 12
• Total: 28
• Multiply ops: 2
• Add ops: 2
• Total: 4
old “wheel”
28 VS 4
new “wheel”
ETC texture
• Ericsson Texture Compression (ETC)
• 6x compression 0f 24-bit RGB data
• No support images with Alpha component
Source: http://en.wikipedia.org/wiki/Ericsson_Texture_Compression
Old “wheel”: std fragment shader
uniform sampler2D uTexture;
varying mediump vec2 vTexCoord;
void main()
{
gl_FragColor = texture2D(uTexture, vTexCoord);
}
We need a “wheel”
ETC texture + Alpha mask texture = ETC with alpha component
Remake the “wheel” for ETC
uniform sampler2D uTexture;
uniform sampler2D uAlpha;
varying mediump vec2 vTexCoord;
void main()
{
vec4 color = texture2D(uTexture, vTexCoord);
vec4 alpha = texture2D(uAlpha, vTexCoord);
gl_FragColor = vec4(color.rgb, alpha.r);
}
• Texture size: 100M • ETC size: 16.6M
• Alpha mask size: 25M
• Total: 41.6M
old “wheel”
100 VS 42
new “wheel”
How to bind 2 textures
and pass them to
fragment shader ?
Old “wheel” was designed for
But we really need
uniform sampler2D uTexture;
uniform mat4 uColorTransformMatrix;
varying mediump vec2 vTexCoord;
void main()
{
Vector4 color = texture2D(uTexture, vTexCoord);
gl_FragColor = uColorTransformMatrix * color;
}
Remake the old “wheel”
• Textures add: 3 • Textures add: 0
old “wheel”
3 VS 0
new “wheel”
How to make the color
transform matrix?
void CMath::MATRIX_3x3_MULTIPLY(float* A, float* B, float* result)
{
result[0] = A[0]*B[0] +A[3]*B[1] + A[6]*B[2];
result[1] = A[1]*B[0] + A[4]*B[1] + A[7]*B[2];
result[2] = A[2]*B[0]+ A[5]*B[1] + A[8]*B[2];
result[3] = A[0]*B[3]+ A[3]*B[4]+ A[6]*B[5];
result[4] = A[1]*B[3] + A[4]*B[4]+ A[7]*B[5];
result[5] = A[2]*B[3]+ A[5]*B[4]+ A[8]*B[5];
result[6] = A[0]*B[6]+ A[3]*B[7]+ A[6]*B[8];
result[7] = A[1]*B[6] + A[4]*B[7]+ A[7]*B[8];
result[8] = A[2]*B[6]+ A[5]*B[7]+ A[8]*B[8];
}
Our old “wheel”
m11 m12 m13
m21 m22 m23
m31 m32 m33
Old “wheel” was designed for
We only need
m11 m12 m13
m21 m22 m23
0 0 1
Remake the old “wheel”
void CMath::MATRIX_3x3_MULTIPLY(float* A, float* B, float* result)
{
result[0] =A[0]*B[0] + A[3]*B[1] + A[6]*0;
result[1] = A[1]*B[0] + A[4]*B[1] + A[7]*0;
result[2] = 0*B[0] + 0*B[1] + 1*0;
result[3] = A[0]*B[3] + A[3]*B[4] + A[6]*0;
result[4] =A[1]*B[3] + A[4]*B[4] + A[7]*0;
result[5] = 0*B[3] + 0*B[4] + 1*0;
result[6] =A[0]*B[6] + A[3]*B[7] + A[6]*1;
result[7] = A[1]*B[6] + A[4]*B[7] + A[7]*1;
result[8] =0*B[6] + 0*B[7] + 1*1;
}
Remake the old “wheel”
void CMath::MATRIX_3x3_MULTIPLY(float* A, float* B, float* result)
{
result[0] =A[0]*B[0] + A[3]*B[1];
result[1] = A[1]*B[0] + A[4]*B[1];
result[3] = A[0]*B[3] + A[3]*B[4];
result[4] =A[1]*B[3] + A[4]*B[4];
result[6] =A[0]*B[6] + A[3]*B[7] + A[6];
result[7] = A[1]*B[6] + A[4]*B[7] + A[7];
}
• Multiply ops: 27
• Add ops: 18
• Total: 45
• Multiply ops: 12
• Add ops: 8
• Total: 20
old “wheel”
45 VS 20
new “wheel”
1 0 tx
0 1 ty
0 0 1
If the new “wheel” only run on
Remake the old “wheel”
void CMath::MATRIX_3x3_MULTIPLY(float* A, float* B, float* result)
{
result[0] =1*B[0] + 0*B[1];
result[1] = 0*B[0] + 1*B[1];
result[3] = 1*B[3] + 0*B[4];
result[4] =0*B[3] + 1*B[4];
result[6] =1*B[6] + 0*B[7] + A[6];
result[7] = 0*B[6] + 1*B[7] + A[7];
}
Remake the old “wheel”
void CMath::MATRIX_3x3_MULTIPLY(float* A, float* B, float* result)
{
result[0] =B[0];
result[1] = B[1];
result[3] = B[3];
result[4] =B[4];
result[6] =B[6] + A[6];
result[7] = B[7] + A[7];
}
Remake the old “wheel”
void CMath::MATRIX_3x3_TRANSLATE(float* M, float tx, float ty)
{
M[6] += tx;
M[7] += ty;
}
• Multiply ops: 27
• Add ops: 18
• Total: 45
• Multiply ops: 0
• Add ops: 2
• Total: 2
old “wheel”
45 VS 2
new “wheel”
sx 0 0
0 sy 0
0 0 1
If the new “wheel” run on
Remake the old “wheel”
void CMath::MATRIX_3x3_SCALE(float* M, float sx, float sy)
{
M[0] *= sx;
M[1] *= sy;
M[3] *= sx;
M[4] *= sy;
M[6] *= sx;
M[7] *= sy;
}
• Multiply ops: 27
• Add ops: 18
• Total: 45
• Multiply ops: 6
• Add ops: 0
• Total: 6
old “wheel”
45 VS 6
new “wheel”
public float[] MakeImageVertexData(float x, float y, short w, short h)
{
xy[0] = x; xy[1] = y;
xy[2] = x; xy[3] = (h + y);
xy[4] = (w + x); xy[5] = (h + y);
xy[6] = (w + x); xy[7] = y;
}
Our old “wheel”
public void RenderImage(…)
{
glPushMatrix();
glScalef(sx, sy, sz);
glTranslatef(tx, ty, tz);
glRotatef(angle, vx, vy, vz);
…
glVertexPointer(2, GL_FLOAT, 0, vertex_data);
glTexCoordPointer(2, GL_FLOAT, 0, tex_coord);
glDrawElements(GL_TRIANGLES, …);
glPopMatrix();
}
Our old “wheel”
MakeImageVertexData(float x, float y, short w, short h,
float m11, float m12, float m21, float m22)
{
xy[0] = x; xy[1] = y;
xy[2] = h*m12 +x; xy[3] = h*m22 + y;
xy[4] = w*m11 + h*m12 + x; xy[5] = w*m21 + h*m22 + y;
xy[6] = w*m11 + x; xy[7] = w*m21 + y;
}
Remake the old “wheel”
public void RenderImage(…)
{
…
glVertexPointer(2, GL_FLOAT, 0, vertex_data);
glTexCoordPointer(2, GL_FLOAT, 0, tex_coord);
glDrawElements(GL_TRIANGLES, …);
…
}
Remake the old “wheel”
old “wheel”
N VS 0
new “wheel”
Our old “wheel”
• Encode binary content to string (Base64)
• Use HTTP GET method
• Decode url string to binary
Our new “wheel”
• No encode
• Use HTTP POST method
- With “Content-Type: application/octet-stream”
- Maybe with "Content-Encoding: gzip“
- And "Content-MD5: …"
• No decode
HttpURLConnection http_conn = (HttpURLConnection)url.openConnection();
http_conn.setRequestMethod(”POST”);
http_conn.setDoOutput(true);
byte[] final_content = GZIP(_binary_data);
http_conn.setRequestProperty("Content-Type", "application/octet-stream");
http_conn.setRequestProperty("Content-Encoding", "gzip");
http_conn.setRequestProperty("Content-MD5", MD5(final_content));
http_conn.setFixedLengthStreamingMode(final_content.length);
//send the POST content
OutputStream out = http_conn.getOutputStream();
out.write(final_content);
out.close();
old “wheel”
100KB
new “wheel”
48KB
Our old “wheel”
function void UpdateMoney(int delta)
{
int money = ReadDB(“userid_money”);
WriteDB(“userid_money”, money + delta);
}
Old “wheel” was
designed for
single thread
But we need
multi-thread !!!
function void UpdateMoney(int delta)
{
1. int money = ReadDB(“userid_money”);
2. WriteDB(“userid_money”, money + delta);
}
• A:1 => A:2 => B:1 => B:2
• A:1 => B:1 => A:2 => B:2
• A:1 => B:1 => B:2 => A:2
• Thread A: -100$
• Thread B: +100$
class CompareAndSet
{
Object value;
long token;
}
new “wheel”
function boolean UpdateMoney(int delta)
{
1. CAS casMoney = CasReadDB(“userid_money”);
2. return CasWriteDB( “userid_money”,
casMoney.value + delta,
casMoney.token);
}
• A:1(t1) => A:2(t2 = t1++) => B:1(t2) => B:2(t3 = t2++)
• A:1(t1) => B:1(t1) => A:2(t2 = t1++) => B:2(t1 != t2)
new “wheel”
old wheel “earn”
±100$
new wheel “earn”
0$
The “wheel” which
you found on internet,
may be not designed for
your “vehicle”.
My exp
Understand your need if you want to remake your “wheel”.
My exp
Remaking usually helps you get a better result.
My exp
Even when it doesn’t get a better result, it always helps you gain more exp.
My exp

Ogdc 2013 lets remake the wheel

  • 1.
  • 2.
  • 6.
    Our old “wheel”:3D vertex shader uniform mat4 uVPMatrix; attribute vec4 aPos; void main() { gl_Position = uVPMatrix * aPos; }
  • 7.
    The old “wheel”was designed for
  • 8.
  • 9.
    Remake the old“wheel” for 2D attribute vec2 aPos; void main() { gl_Position = vec4( aPos.x/HALF_WIDTH - 1.0, 1.0 - aPos.y/HALF_HEIGHT, 0.0, 1.0); }
  • 10.
    • Multiply ops:16 • Add ops: 12 • Total: 28 • Multiply ops: 2 • Add ops: 2 • Total: 4 old “wheel” 28 VS 4 new “wheel”
  • 12.
    ETC texture • EricssonTexture Compression (ETC) • 6x compression 0f 24-bit RGB data • No support images with Alpha component Source: http://en.wikipedia.org/wiki/Ericsson_Texture_Compression
  • 13.
    Old “wheel”: stdfragment shader uniform sampler2D uTexture; varying mediump vec2 vTexCoord; void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }
  • 14.
    We need a“wheel” ETC texture + Alpha mask texture = ETC with alpha component
  • 15.
    Remake the “wheel”for ETC uniform sampler2D uTexture; uniform sampler2D uAlpha; varying mediump vec2 vTexCoord; void main() { vec4 color = texture2D(uTexture, vTexCoord); vec4 alpha = texture2D(uAlpha, vTexCoord); gl_FragColor = vec4(color.rgb, alpha.r); }
  • 16.
    • Texture size:100M • ETC size: 16.6M • Alpha mask size: 25M • Total: 41.6M old “wheel” 100 VS 42 new “wheel”
  • 17.
    How to bind2 textures and pass them to fragment shader ?
  • 19.
    Old “wheel” wasdesigned for
  • 20.
  • 21.
    uniform sampler2D uTexture; uniformmat4 uColorTransformMatrix; varying mediump vec2 vTexCoord; void main() { Vector4 color = texture2D(uTexture, vTexCoord); gl_FragColor = uColorTransformMatrix * color; } Remake the old “wheel”
  • 22.
    • Textures add:3 • Textures add: 0 old “wheel” 3 VS 0 new “wheel”
  • 23.
    How to makethe color transform matrix?
  • 25.
    void CMath::MATRIX_3x3_MULTIPLY(float* A,float* B, float* result) { result[0] = A[0]*B[0] +A[3]*B[1] + A[6]*B[2]; result[1] = A[1]*B[0] + A[4]*B[1] + A[7]*B[2]; result[2] = A[2]*B[0]+ A[5]*B[1] + A[8]*B[2]; result[3] = A[0]*B[3]+ A[3]*B[4]+ A[6]*B[5]; result[4] = A[1]*B[3] + A[4]*B[4]+ A[7]*B[5]; result[5] = A[2]*B[3]+ A[5]*B[4]+ A[8]*B[5]; result[6] = A[0]*B[6]+ A[3]*B[7]+ A[6]*B[8]; result[7] = A[1]*B[6] + A[4]*B[7]+ A[7]*B[8]; result[8] = A[2]*B[6]+ A[5]*B[7]+ A[8]*B[8]; } Our old “wheel”
  • 26.
    m11 m12 m13 m21m22 m23 m31 m32 m33 Old “wheel” was designed for
  • 27.
    We only need m11m12 m13 m21 m22 m23 0 0 1
  • 28.
    Remake the old“wheel” void CMath::MATRIX_3x3_MULTIPLY(float* A, float* B, float* result) { result[0] =A[0]*B[0] + A[3]*B[1] + A[6]*0; result[1] = A[1]*B[0] + A[4]*B[1] + A[7]*0; result[2] = 0*B[0] + 0*B[1] + 1*0; result[3] = A[0]*B[3] + A[3]*B[4] + A[6]*0; result[4] =A[1]*B[3] + A[4]*B[4] + A[7]*0; result[5] = 0*B[3] + 0*B[4] + 1*0; result[6] =A[0]*B[6] + A[3]*B[7] + A[6]*1; result[7] = A[1]*B[6] + A[4]*B[7] + A[7]*1; result[8] =0*B[6] + 0*B[7] + 1*1; }
  • 29.
    Remake the old“wheel” void CMath::MATRIX_3x3_MULTIPLY(float* A, float* B, float* result) { result[0] =A[0]*B[0] + A[3]*B[1]; result[1] = A[1]*B[0] + A[4]*B[1]; result[3] = A[0]*B[3] + A[3]*B[4]; result[4] =A[1]*B[3] + A[4]*B[4]; result[6] =A[0]*B[6] + A[3]*B[7] + A[6]; result[7] = A[1]*B[6] + A[4]*B[7] + A[7]; }
  • 30.
    • Multiply ops:27 • Add ops: 18 • Total: 45 • Multiply ops: 12 • Add ops: 8 • Total: 20 old “wheel” 45 VS 20 new “wheel”
  • 31.
    1 0 tx 01 ty 0 0 1 If the new “wheel” only run on
  • 32.
    Remake the old“wheel” void CMath::MATRIX_3x3_MULTIPLY(float* A, float* B, float* result) { result[0] =1*B[0] + 0*B[1]; result[1] = 0*B[0] + 1*B[1]; result[3] = 1*B[3] + 0*B[4]; result[4] =0*B[3] + 1*B[4]; result[6] =1*B[6] + 0*B[7] + A[6]; result[7] = 0*B[6] + 1*B[7] + A[7]; }
  • 33.
    Remake the old“wheel” void CMath::MATRIX_3x3_MULTIPLY(float* A, float* B, float* result) { result[0] =B[0]; result[1] = B[1]; result[3] = B[3]; result[4] =B[4]; result[6] =B[6] + A[6]; result[7] = B[7] + A[7]; }
  • 34.
    Remake the old“wheel” void CMath::MATRIX_3x3_TRANSLATE(float* M, float tx, float ty) { M[6] += tx; M[7] += ty; }
  • 35.
    • Multiply ops:27 • Add ops: 18 • Total: 45 • Multiply ops: 0 • Add ops: 2 • Total: 2 old “wheel” 45 VS 2 new “wheel”
  • 36.
    sx 0 0 0sy 0 0 0 1 If the new “wheel” run on
  • 37.
    Remake the old“wheel” void CMath::MATRIX_3x3_SCALE(float* M, float sx, float sy) { M[0] *= sx; M[1] *= sy; M[3] *= sx; M[4] *= sy; M[6] *= sx; M[7] *= sy; }
  • 38.
    • Multiply ops:27 • Add ops: 18 • Total: 45 • Multiply ops: 6 • Add ops: 0 • Total: 6 old “wheel” 45 VS 6 new “wheel”
  • 40.
    public float[] MakeImageVertexData(floatx, float y, short w, short h) { xy[0] = x; xy[1] = y; xy[2] = x; xy[3] = (h + y); xy[4] = (w + x); xy[5] = (h + y); xy[6] = (w + x); xy[7] = y; } Our old “wheel”
  • 41.
    public void RenderImage(…) { glPushMatrix(); glScalef(sx,sy, sz); glTranslatef(tx, ty, tz); glRotatef(angle, vx, vy, vz); … glVertexPointer(2, GL_FLOAT, 0, vertex_data); glTexCoordPointer(2, GL_FLOAT, 0, tex_coord); glDrawElements(GL_TRIANGLES, …); glPopMatrix(); } Our old “wheel”
  • 42.
    MakeImageVertexData(float x, floaty, short w, short h, float m11, float m12, float m21, float m22) { xy[0] = x; xy[1] = y; xy[2] = h*m12 +x; xy[3] = h*m22 + y; xy[4] = w*m11 + h*m12 + x; xy[5] = w*m21 + h*m22 + y; xy[6] = w*m11 + x; xy[7] = w*m21 + y; } Remake the old “wheel”
  • 43.
    public void RenderImage(…) { … glVertexPointer(2,GL_FLOAT, 0, vertex_data); glTexCoordPointer(2, GL_FLOAT, 0, tex_coord); glDrawElements(GL_TRIANGLES, …); … } Remake the old “wheel”
  • 44.
    old “wheel” N VS0 new “wheel”
  • 46.
    Our old “wheel” •Encode binary content to string (Base64) • Use HTTP GET method • Decode url string to binary
  • 47.
    Our new “wheel” •No encode • Use HTTP POST method - With “Content-Type: application/octet-stream” - Maybe with "Content-Encoding: gzip“ - And "Content-MD5: …" • No decode
  • 48.
    HttpURLConnection http_conn =(HttpURLConnection)url.openConnection(); http_conn.setRequestMethod(”POST”); http_conn.setDoOutput(true); byte[] final_content = GZIP(_binary_data); http_conn.setRequestProperty("Content-Type", "application/octet-stream"); http_conn.setRequestProperty("Content-Encoding", "gzip"); http_conn.setRequestProperty("Content-MD5", MD5(final_content)); http_conn.setFixedLengthStreamingMode(final_content.length); //send the POST content OutputStream out = http_conn.getOutputStream(); out.write(final_content); out.close();
  • 49.
  • 51.
    Our old “wheel” functionvoid UpdateMoney(int delta) { int money = ReadDB(“userid_money”); WriteDB(“userid_money”, money + delta); }
  • 52.
    Old “wheel” was designedfor single thread But we need multi-thread !!!
  • 53.
    function void UpdateMoney(intdelta) { 1. int money = ReadDB(“userid_money”); 2. WriteDB(“userid_money”, money + delta); } • A:1 => A:2 => B:1 => B:2 • A:1 => B:1 => A:2 => B:2 • A:1 => B:1 => B:2 => A:2 • Thread A: -100$ • Thread B: +100$
  • 54.
  • 55.
    function boolean UpdateMoney(intdelta) { 1. CAS casMoney = CasReadDB(“userid_money”); 2. return CasWriteDB( “userid_money”, casMoney.value + delta, casMoney.token); } • A:1(t1) => A:2(t2 = t1++) => B:1(t2) => B:2(t3 = t2++) • A:1(t1) => B:1(t1) => A:2(t2 = t1++) => B:2(t1 != t2) new “wheel”
  • 56.
  • 57.
    The “wheel” which youfound on internet, may be not designed for your “vehicle”. My exp
  • 58.
    Understand your needif you want to remake your “wheel”. My exp
  • 59.
    Remaking usually helpsyou get a better result. My exp
  • 60.
    Even when itdoesn’t get a better result, it always helps you gain more exp. My exp

Editor's Notes

  • #6 Vertex Shader - OpenGL ES graphics code for rendering the vertices of a shape.Fragment Shader - OpenGL ES code for rendering the face of a shape with colors or textures.Program - An OpenGL ES object that contains the shaders you want to use for drawing one or more shapes.
  • #12 What’s ETC ?Why should we use ETC texture ?But ETC don’t support alpha channel !!!
  • #16 Q&A
  • #18 Load texturefor (inti = 0 ; i < n ; ++i) { glActiveTexture(GL_TEXTURE0 + i); glBindTexture(GL_TEXTURE_2D, texture_ids[i]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, ...); }Use texturefor (i = 0 ; i < n; ++i) { ... glUniform1i(texture_location, i); ... glDrawElements(...); }
  • #24 References:http://beesbuzz.biz/code/hsv_color_transforms.phphttp://www.rapidtables.com/convert/color/rgb-to-hsv.htmhttp://www.cs.rit.edu/~ncs/color/t_convert.html