Game DevLog Series #3 - Finding Corners and Edges
Sometimes I get hung up on tiny little things. I feel like I could do better on this code or this. And I often think is this efficient enough? Does it performs good? And I really want to tweak things.
Currently I'm hung up on this little corner finding function:
NinePatchDrawable::orientation NinePatchDrawable::get_orientation(int xl, int yl, int wl, int hl) {
orientation o;
if(yl == 0) {
if (xl == 0) {
o = NW;
} else if (xl == wl - 1) {
o = NE;
} else {
o = N;
}
} else if (yl == hl - 1) {
if(xl == 0) {
o = SW;
} else if (xl == wl - 1) {
o = SE;
} else {
o = S;
}
} else if (xl == 0) {
o = W;
} else if(xl == (wl - 1)) {
o = E;
} else {
o = CENTER;
}
return o;
}
Don't get me wrong it works. But all those IF/ELSEs gets me frustrated. So I was thinking OK... what is the compiler actually doing with this funny code?
You can either explore it in your IDE or in my case I'm using CLion and currently CLion does not support jumping into assembly. So I just went to: https://godbolt.org/
Without optimization its as you've might expected to be:
get_orientation(int, int, int, int): # @get_orientation(int, int, int, int)
push rbp
mov rbp, rsp
mov dword ptr [rbp - 4], edi
mov dword ptr [rbp - 8], esi
mov dword ptr [rbp - 12], edx
mov dword ptr [rbp - 16], ecx
cmp dword ptr [rbp - 8], 0
jne .LBB0_8
cmp dword ptr [rbp - 4], 0
jne .LBB0_3
mov dword ptr [rbp - 20], 0
jmp .LBB0_7
.LBB0_3:
mov eax, dword ptr [rbp - 4]
mov ecx, dword ptr [rbp - 12]
sub ecx, 1
cmp eax, ecx
jne .LBB0_5
mov dword ptr [rbp - 20], 2
jmp .LBB0_6
.LBB0_5:
mov dword ptr [rbp - 20], 1
.LBB0_6:
jmp .LBB0_7
.LBB0_7:
jmp .LBB0_24
.LBB0_8:
mov eax, dword ptr [rbp - 8]
mov ecx, dword ptr [rbp - 16]
sub ecx, 1
cmp eax, ecx
jne .LBB0_16
cmp dword ptr [rbp - 4], 0
jne .LBB0_11
mov dword ptr [rbp - 20], 6
jmp .LBB0_15
.LBB0_11:
mov eax, dword ptr [rbp - 4]
mov ecx, dword ptr [rbp - 12]
sub ecx, 1
cmp eax, ecx
jne .LBB0_13
mov dword ptr [rbp - 20], 4
jmp .LBB0_14
.LBB0_13:
mov dword ptr [rbp - 20], 5
.LBB0_14:
jmp .LBB0_15
.LBB0_15:
jmp .LBB0_23
.LBB0_16:
cmp dword ptr [rbp - 4], 0
jne .LBB0_18
mov dword ptr [rbp - 20], 7
jmp .LBB0_22
.LBB0_18:
mov eax, dword ptr [rbp - 4]
mov ecx, dword ptr [rbp - 12]
sub ecx, 1
cmp eax, ecx
jne .LBB0_20
mov dword ptr [rbp - 20], 3
jmp .LBB0_21
.LBB0_20:
mov dword ptr [rbp - 20], 8
.LBB0_21:
jmp .LBB0_22
.LBB0_22:
jmp .LBB0_23
.LBB0_23:
jmp .LBB0_24
.LBB0_24:
mov eax, dword ptr [rbp - 20]
pop rbp
ret
I mean look at those JMPs :D... of course with -O3 it does not look that bad at all:
get_orientation(int, int, int, int): # @get_orientation(int, int, int, int)
test esi, esi
je .LBB0_1
add ecx, -1
cmp ecx, esi
jne .LBB0_8
test edi, edi
je .LBB0_6
add edx, -1
xor eax, eax
cmp edx, edi
sete al
xor eax, 5
ret
.LBB0_1:
test edi, edi
je .LBB0_2
add edx, -1
xor eax, eax
cmp edx, edi
sete al
add eax, 1
ret
.LBB0_8:
test edi, edi
je .LBB0_9
add edx, -1
xor eax, eax
cmp edx, edi
setne al
lea eax, [rax + 4*rax]
add eax, 3
ret
.LBB0_2:
xor eax, eax
ret
.LBB0_6:
mov eax, 6
ret
.LBB0_9:
mov eax, 7
ret
we have some jumps, but not so much as before, and we only have forward jumps, thats good. Maybe this code is already really good, It does not look bad at all. I'm really impressed what compilers can do nowadays. But I thought, maybe we can do some arithmetic to find the corners and maybe this code will compile a little bit better.
You might think, ok why am I doing this? Well mostly because its fun to optimize those little code sections and to find code that performs a little better. It won't do any performance improvements to my game. I'm certain of it. But it's fun.
Conclusion
Now I'm certain that I'll just write code without overthinking it too hard. Let the compiler do the optimizing stuff and write code that you understand at a first glance. Without bit-fiddling or some really cool hacky things.