Developing games in Go�for Nintendo Switch™
(English ver.)
Go Conference 2021 Autumn (2021-11-13)
Hajime Hoshi
Nintendo Switch is a registered trademark of Nintendo Co., Ltd.
Caution
Agenda
Self-introduction
About Ebiten
Ebiten
Ebiten features
Go Gopher by Renee French is licensed under the Creative Commons Attribution 3.0 License
Ebiten for Nintendo Switch demo
Bear's Restaurant
Copyright 2021 Odencat Inc.
Others (Odencat)
Copyright 2021 Odencat Inc.
Ebiten for Nintendo Switch
Copyright 2021 Odencat Inc.
What I wanted to do
What I wanted to do
We want to make "Bear's Restaurant"�work on Nintendo Switch
Bear's Restaurant
Copyright 2021 Odencat Inc.
Limitations of Go
Limitations of Nintendo Switch
Copyright いらすとや
"Secret"
What should I do?
A game
Ebiten
Go runtime
System calls
C functions
Nintendo Switch's original API
Fill this gap
?
Go and system calls
Limitations by the engineer
Copyright いらすとや
Summary
A game
Ebiten
Go Runtime
Syscalls
C
Nintendo Switch API
Fill this gap
?
What I did
What I did
Choosing an approach
A Go program
A modified Go compiler
The regular Go compiler
C++
A binary
An original Go compiler
A regular Go binary
Some transforms
1
2
3
4
5
6
1. Go → (An original compiler) → A binary
A Go program
A binary
An original Go compiler
2. Go → (An original compiler) → C++
A Go program
C++
An original Go compiler
3. Go → (A modified Go compiler) → A binary
A Go program
A binary
A modified Go compiler
4. Go → (A modified Go compiler) → C++
A Go program
C++
A modified Go compiler
5. Go→(A Go compiler)→A Go binary→(Trans.)→A binary
A Go program
A regular Go binary
The regular Go compiler
A binary
Some transforms
6. Go→(A Go compiler)→A Go binary→(Trans.)→C++
A Go program
A regular Go binary
The regular Go compiler
C++
Some transforms
The chosen approach: go2cpp
A Go program
A modified Go compiler
The regular Go compiler
C++
A binary
An original Go compiler
A Wasm binary
go2cpp
1
2
3
4
5
6
go2cpp
Wasm (WebAssembly)
The WebAssembly logo is licensed under CC0 1.0 Universal
Why was Wasm adopted as the intermediate format?
Comparing GOOS=js GOARCH=wasm and go2cpp
A game�Ebiten�The Go runtime�syscall/js�(Wasm)
wasm_exec.js
import functions (JavaScript)
Web API
C++ for wasm_exec.js
import functions (C++)
Nintendo Switch's API
A game�Ebiten�The Go runtime�syscall/js�(C++ converted from Wasm)
Nintendo Switch driver
Converted by go2cpp
Secret
Generated by go2cpp
go2cpp
C++ for wasm_exec.js
import functions (C++)
Nintendo Switch's API
A game�Ebiten�The Go runtime�syscall/js�(C++ converted from Wasm)
Nintendo Switch driver
I'm now working on�browsers...
Wasm → C++
local.get 1
global.get 2
i32.wrap_i64
i32.load offset=16
i32.le_u
if ;; label = @55
local.get 1
i32.const 8
i32.sub
local.tee 1
global.set 0
local.get 1
i64.const 357761024
i64.store
i32.const 0
call $runtime.morestack_noctxt
global.get 0
local.set 1
br_if 54 (;@1;)
end
if (static_cast<uint32_t>(local1_) <= static_cast<uint32_t>(mem_->LoadInt32((static_cast<int32_t>(global2_)) + 16))) {
i32_0_ = (static_cast<int32_t>(static_cast<uint32_t>(local1_) - static_cast<uint32_t>(8)));
local1_ = i32_0_;
global0_ = i32_0_;
mem_->StoreInt64((local1_), 357761024LL);
i32_1_ = runtime_2emorestack_5fnoctxt((0));
local1_ = global0_;
if (i32_1_) {
return 1;
}
}
Replace the stack machine expressions with C++'s expressions
Modifying wasm_exec.js
Imitating a part of Web API
Binding (communicating between C++ and Go)
class Binding : public
go2cpp_autogen::Game::Binding {
std::vector<uint8_t> Get(const std::string &key)� override {
if (key == "version") {
std::string version_str = "1.0.0";
return std::vector<uint8_t>(� version_str.begin(), version_str.end());
}
return {};
}
};
extern "C" void main() {
go2cpp_autogen::Game game(� std::make_unique<NintendoSwitchDriver>(),
std::make_unique<Binding>());
game.Run({"your_game"});
}
func version() string {
// syscall/js is available
go2cpp := js.Global().Get("go2cpp")
if !go2cpp.Truthy() {
return ""
}
// binding's Get always returns Uint8Array
ver :=� go2cpp.Get("binding").Get("version")
bs := make([]byte, � ver.Get("byteLength").Int())
js.CopyBytesToGo(bs, ver)
// "1.0.0"
return string(bs)
}
syscall/js is available
Why go2cpp?
Go Gopher by Renee French is licensed under the Creative Commons Attribution 3.0 License
Why go2cpp? 1. A general judgement
Various aspects in software development
In the case of go2cpp
Why go2cpp? 2. Making the product open
Summary
C++ for wasm_exec.js
import functions (C++)
Nintendo Switch's API
A game�Ebiten�The Go runtime�syscall/js�(C++ converted from Wasm)
Nintendo Switch driver
Result
Result
Development time
Performance
Benchmark result of fmt package
name old time/op new time/op delta
SprintfEmpty 18.8ns ± 1% 111.3ns ± 3% +490.87% (p=0.008 n=5+5)
SprintfComplex 473ns ± 2% 1247ns ± 1% +163.90% (p=0.008 n=5+5)
name old time/op new time/op delta
SprintfPrefixedInt 464ns ± 1% 597ns ± 2% +28.69% (p=0.008 n=5+5)
ScanInts 1.05ms ± 5% 0.85ms ± 7% -19.09% (p=0.008 n=5+5)
The issue of performance
GC issues
Copyright いらすとや
Methods for GC 1. Manual GC
debug.SetGCPercent(-1)
go func() {
const threshold = 500 << 20
var stats runtime.MemStats
for {
runtime.ReadMemStats(&stats)
if stats.HeapAlloc > threshold {
runtime.GC()
}
time.Sleep(time.Second)
}
}()
Methods for GC 2. Reducing heap allocations
Methods for GC 3. Other techniques
Future works
Summary
Summary
A Go program
A Wasm binary
The Go compiler
C++
go2cpp
Copyright 2021 Odencat Inc.
Bear's Restaurant for Switch