blob: e74a2ccceffde3a4aa74a93cadbce4fee9519359 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/metal_util/test_shader.h"
#import <Metal/Metal.h>
#include "base/bind.h"
#include "base/debug/dump_without_crashing.h"
#include "base/mac/scoped_nsobject.h"
#include "base/memory/ref_counted.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
#include "base/task/post_task.h"
#include "components/metal_util/device.h"
namespace metal {
namespace {
const char* kTestShaderSource =
""
"#include <metal_stdlib>\n"
"#include <simd/simd.h>\n"
"typedef struct {\n"
" float4 clipSpacePosition [[position]];\n"
" float4 color;\n"
"} RasterizerData;\n"
"\n"
"vertex RasterizerData vertexShader(\n"
" uint vertexID [[vertex_id]],\n"
" constant vector_float2 *positions[[buffer(0)]],\n"
" constant vector_float4 *colors[[buffer(1)]]) {\n"
" RasterizerData out;\n"
" out.clipSpacePosition = vector_float4(0.0, 0.0, 0.0, 1.0);\n"
" out.clipSpacePosition.xy = positions[vertexID].xy;\n"
" out.color = colors[vertexID];\n"
" return out;\n"
"}\n"
"\n"
"fragment float4 fragmentShader(RasterizerData in [[stage_in]]) {\n"
" return %f * in.color;\n"
"}\n"
"";
// State shared between the compiler callback and the caller.
class API_AVAILABLE(macos(10.11)) TestShaderState
: public base::RefCountedThreadSafe<TestShaderState> {
public:
TestShaderState(TestShaderCallback callback)
: callback_(std::move(callback)) {}
void RunCallback(TestShaderResult result) {
TestShaderCallback callback;
{
base::AutoLock lock(lock_);
callback = std::move(callback_);
}
if (callback)
std::move(callback).Run(result);
}
protected:
base::Lock lock_;
TestShaderCallback callback_;
friend class base::RefCountedThreadSafe<TestShaderState>;
virtual ~TestShaderState() {}
};
} // namespace
void TestShader(float seed,
TestShaderCallback callback,
const base::TimeDelta& timeout) {
if (@available(macOS 10.11, *)) {
base::scoped_nsprotocol<id<MTLDevice>> device(CreateDefaultDevice());
if (device) {
auto state = base::MakeRefCounted<TestShaderState>(std::move(callback));
const std::string shader_source =
base::StringPrintf(kTestShaderSource, seed);
base::scoped_nsobject<NSString> source([[NSString alloc]
initWithCString:shader_source.c_str()
encoding:NSASCIIStringEncoding]);
base::scoped_nsobject<MTLCompileOptions> options(
[[MTLCompileOptions alloc] init]);
MTLNewLibraryCompletionHandler completion_handler =
^(id<MTLLibrary> library, NSError* error) {
if (library)
state->RunCallback(TestShaderResult::kSucceeded);
else
state->RunCallback(TestShaderResult::kFailed);
};
[device newLibraryWithSource:source
options:options
completionHandler:completion_handler];
base::PostDelayedTask(FROM_HERE, {base::ThreadPool()},
base::BindOnce(&TestShaderState::RunCallback, state,
TestShaderResult::kTimedOut),
timeout);
return;
}
}
std::move(callback).Run(TestShaderResult::kNotAttempted);
}
} // namespace metal