What I'm trying to do
I have an [[Int]] in Swift and I'm trying to pass it into Metal through a buffer. Basically, I'm trying to use Metal to add two matrices multithreaded and give them back to Swift. So far, this is more difficult than expected.
The problem
Metal tells me that my graph isn't a pointer and I suspect I'm passing it into Metal wrong.
import MetalKit
let graph: [[Int]] = [
[0, 1, 2, 999, 999, 999],
[1, 0, 999, 5, 1, 999],
[2, 999, 0, 2, 3, 999],
[999, 5, 2, 0, 2, 2],
[999, 1, 3, 2, 0, 1],
[999, 999, 999, 2, 1, 0]]
func fooFunc(gra: [[Int]]) {
let count = gra.count
let device = MTLCreateSystemDefaultDevice()
let commandQueue = device?.makeCommandQueue()
let gpuFunctionLibrary = device?.makeDefaultLibrary()
let funcGPUFunction = gpuFunctionLibrary?.makeFunction(name: "MetalFunc")
var funcPipelineState: MTLComputePipelineState!
do {
funcPipelineState = try device?.makeComputePipelineState(function: funcGPUFunction!)
} catch {
print(error)
}
let graphBuff = device?.makeBuffer(bytes: gra,
length: MemoryLayout<Int>.size * count * count,
options: .storageModeShared)
let resultBuff = device?.makeBuffer(length: MemoryLayout<Int>.size * count,
options: .storageModeShared)
let commandBuffer = commandQueue?.makeCommandBuffer()
let commandEncoder = commandBuffer?.makeComputeCommandEncoder()
commandEncoder?.setComputePipelineState(additionComputePipelineState)
commandEncoder?.setBuffer(graphBuff, offset: 0, index: 0)
commandEncoder?.setBuffer(resultBuff, offset: 0, index: 1)
let threadsPerGrid = MTLSize(width: count, height: 1, depth: 1)
let maxThreadsPerThreadgroup = additionComputePipelineState.maxTotalThreadsPerThreadgroup // 1024
let threadsPerThreadgroup = MTLSize(width: maxThreadsPerThreadgroup, height: 1, depth: 1)
commandEncoder?.dispatchThreads(threadsPerGrid,
threadsPerThreadgroup: threadsPerThreadgroup)
commandEncoder?.endEncoding()
commandBuffer?.commit()
commandBuffer?.waitUntilCompleted()
let resultBufferPointer = resultBuff?.contents().bindMemory(to: Int.self,
capacity: MemoryLayout<Int>.size * count)
print("Result: \(Int(resultBufferPointer!.pointee) as Any)")
}
gpuDijkstra(gra: graph)
#include <metal_stdlib>
using namespace metal;
kernel void MetalFunc(constant int *graph [[ buffer(0) ]],
constant int *result [[ buffer(1) ]],
uint index [[ thread_position_in_grid ]]
{
const int size = sizeof(*graph);
int result[size][size];
for(int k = 0; k<size; k++){
result[index][k]=graph[index][k]+graph[index][k]; //ERROR: Subscripted value is not an array, pointer, or vector
}
}
I don't know about the Swift implementation but your kernel function is wrong.
If you want to write to and read from the same buffer you should use Device address space attribute.
The device address space name refers to buffer memory objects
allocated from the device memory pool that are both readable and
writeable.
device int *result /* write and read */
constant int *graph /* read only */
This is the correct implementation:
#include <metal_stdlib>
using namespace metal;
kernel void MetalFunc(constant int *graph [[ buffer(0) ]],
device int *result [[ buffer(1) ]],
uint index [[ thread_position_in_grid ]]
{
/* Point to an array (graph buffer) */
constant int(*r)[6][6] = (constant int(*)[6][6])graph;
/* Point to an array (result buffer) */
device int(*w)[6][6] = (device int(*)[6][6])result;
/* Read the first element */
int i = (*r)[0][0];
(*w)[0][0] = 777; /* Write to the first element */
(*w)[5][5] = 999; /* Write to the last element */
}
I'm coding in C++ and making use of the library.
My code goes something like this:
struct request {
int order;
float size;
};
typedef struct request req;
.. ...
std::queue <req> wait_queue;
// ...
for (int i = 0; i < 5; i++) {
req r;
r.order = i + 1;
r.size = 100;
wait_queue.push(r);
}
// ...
if (!wait_queue.empty()) {
req r = wait_queue.front();
// do something...
}
Where the req r do not conflict because they're defined in different functions in actuality.
Now I know the variables are saved correctly because when I run gdb and type p wait_queue it shows:
(gdb) $1 = std::queue wrapping: std::deque with 5 elements = {{order = 1, size = 100}, {order = 2, size = 100}, {order = 3,
size = 100}, {order = 4, size = 100}, {order = 5, size = 100}}
But after r = wait_queue.front(), p r shows:
(gdb) $2 = {order = 6417952, size = 0}
So I want to know how this can happen, and what I can do to make sure r will hold {order = 1, size = 100}.
Thanks in advance!
I want to modify values for a matrix that I previously don't its size:
ArrayFactory factory;
std::vector<size_t> position = {3, 14, 2, 23};
Array mat_data = factory.createArray<double>({30, 30, 30, 30});
Reference<TypedArray<double>> temp = mat_data[position[0]]; //fails here
for (int i = 1; i < positions.size(); ++i)
mat_temp = mat_temp[positions[i]];
mat_temp[positions.back()] = getMyValue(position);
This gives me the following error:
Not enough indices provided
I know that I'm able to do this:
mat_data[3][14][2][23] = getMyValue(position);
But I can't hard code this, because I don't know the size of position when the algorithm starts.
Actual solution removing the private that hides ArrayElementRef(ArrayElementRef<is_const_ref>&& rhs) MW_NOEXCEPT on ArrayElementRef.hpp:
ArrayElementRef<false> getElementRef(ArrayElementRef<false> elem, std::vector<size_t>& position, size_t pos) {
if (pos >= position.size() - 1)
return std::move(elem);
return getElementRef(elem[position[pos]], position, ++pos);
}
And then:
ArrayFactory factory;
std::vector<size_t> position = {3, 14, 2, 23};
Array mat_data = factory.createArray<double>({30, 30, 30, 30});
ArrayElementRef<false> mat_temp = getElementRef(mat_data[position[0]], positions, 1);
mat_temp[position.back()] = getMyValue(position);
But, removing this private is not desirable...
I'm trying to export application data as xls chart.
I'm doing it using SimpleXlsxWriter.
But I have some problems with this lib.
Here's what I got using this lib
#include <Xlsx/Workbook.h>
int main()
{
using namespace SimpleXlsx;
CWorkbook book;
CWorksheet &data = book.AddSheet("Data");
std::vector<CellDataDbl> header = { 1, 3, 5, 6, 8 };
std::vector<CellDataDbl> data1 = { 2, 6, 4, 8, 5 };
std::vector<CellDataDbl> data2 = { 5, 3, 5, 2, 4 };
data.AddRow(header);
data.AddRow(data1);
data.AddRow(data2);
CChartsheet &chart = book.AddChart("Chart", CHART_BAR);
chart.SetBarDirection(CChartsheet::BAR_DIR_VERTICAL);
chart.SetBarGrouping(CChartsheet::BAR_GROUP_STACKED);
chart.SetTableDataState(CChartsheet::TBL_DATA);
chart.SetLegendPos(CChartsheet::EPosition::POS_TOP);
CChartsheet::Series ser1;
ser1.valAxisFrom = CellCoord(1, 0);
ser1.valAxisTo = CellCoord(1, data1.size() - 1);
ser1.valSheet = &data;
ser1.title = "Ser1";
CChartsheet::Series ser2;
ser2.valAxisFrom = CellCoord(2, 0);
ser2.valAxisTo = CellCoord(2, data2.size() - 1);
ser2.valSheet = &data;
ser2.title = "Ser2";
chart.AddSeries(ser1);
chart.AddSeries(ser2);
book.Save("test.xls");
}
I didn't find the ways how to show y-axes, show horizontal lines, set x-axes values, insert the chart into the data sheet, etc.
Here's what do I want to get as a result
What a library can help me or how can I get a required result? Maybe SimpleXlsxWriter can do that?
You can get what you want from the SimpleXlsxWriter, just with a few modifications of your code. Briefly:
save with SimpleXlsxWriter's format .xlsx not .xls
Add a category axis to one of your series and set it to first data row
use BAR_GROUP_PERCENT_STACKED since you want the y axis to be on % scale
You can control the order you add the series the the chart, to get the order you want in the bottom table
Consider this code, suggested modifications are indicated in comments:
int main()
{
using namespace SimpleXlsx;
CWorkbook book;
CWorksheet &data = book.AddSheet("Data");
std::vector<CellDataDbl> header = { 1, 3, 5, 6, 8 };
std::vector<CellDataDbl> data1 = { 2, 6, 4, 8, 5 };
std::vector<CellDataDbl> data2 = { 5, 3, 5, 2, 4 };
data.AddRow(header);
data.AddRow(data1);
data.AddRow(data2);
CChartsheet &chart = book.AddChart("Chart", CHART_BAR);
chart.SetBarDirection(CChartsheet::BAR_DIR_VERTICAL);
chart.SetBarGrouping(CChartsheet::BAR_GROUP_PERCENT_STACKED); // <-- seems you want this format?
chart.SetTableDataState(CChartsheet::TBL_DATA);
chart.SetLegendPos(CChartsheet::EPosition::POS_TOP);
chart.SetYAxisGrid(CChartsheet::EGridLines::GRID_MAJOR); // <-- to draw the horizontal lines
CChartsheet::Series ser1;
ser1.valSheet = &data;
ser1.valAxisFrom = CellCoord(1, 0);
ser1.valAxisTo = CellCoord(1, data1.size() - 1);
ser1.title = "Ser1";
// Now add a category axis from your the first row of the data sheet
ser1.catAxisFrom = CellCoord(0, 0);
ser1.catAxisTo = CellCoord(0, data1.size() - 1);
ser1.catSheet = &data;
CChartsheet::Series ser2;
ser2.valSheet = &data;
ser2.valAxisFrom = CellCoord(2, 0);
ser2.valAxisTo = CellCoord(2, data2.size() - 1);
ser2.title = "Ser2";
// insert the series in the order you want from the bottom up in the chart's table
chart.AddSeries(ser2);
chart.AddSeries(ser1);
book.Save("c:\\so\\test.xlsx"); // <-- Save as xlsx, not xls
}
I'm having a problem when I try this code I've made:
int ledStart = 30;
boolean commonHigh = true;
void setup() {
Serial.begin(115200);
SetTimer(0, 0, 10); // 10 seconds
StartTimer();
for (int i =0;i<9;++i) {
pinMode (i, OUTPUT);
}
pinMode(9, INPUT);
}
int counter = 0;
bool go_by_switch = true;
int last_input_value = LOW;
void loop() {
// put your main code here, to run repeatedly:
number++;
delay(1000);
if(number>9)
number=0; // If number is bigger than 9, then number is 0
}
// 0 6
// pins A B C D E F G
int ledpins[] = {12, 10, 7, 4, 2, 13, 8};
int pincnt = 7;
int number = 0;
int sevenseg[10][7] = {
// A, B, C, D, E, F, G
{1, 1, 1, 1, 1, 1, 0}, // A-F shall light. G shall not light.
{0, 1, 1, 0, 0, 0, 0}, // A shall not light. B and C shall light.
/*0*/
/*1*/
/*2*/
/*3*/
/*4*/
/*5*/
/*6*/
/*7*/
/*8*/
{1, 1, 1, 1, 1, 1, 1, 1}
if(go_by_switch) {
int switch_input_value = digitalRead(9);
if(last_input_value == LOW && switch_input_value == HIGH) {
counter = (counter + 1) % 10;
}
last_input_value = switch_input_value;
}
else {
delay(500);
counter = (counter + 1) % 10;
}
writeNumber(counter);
}
for (int p=0; p<pincnt; p++) {
pinMode (ledpins[P], OUTPUT);
//It will count from 0 to smaller than 7. {12, 10, 7, 4, 2, 13, 8}; It will count from 0 to smaller than 7.
// 0 1 2 3 4 5 6
digitalWrite(ledpins[P], LOW);
}
for (int x=0; x<pincnt; x++); { //x is smaller than 7. The point is to bring out one of the patterns that will show on the display
if (sevenseg[number][x]) // sevenseg = 7-segment display
digitalWrite (ledpins[x], HIGH); // If it is 1, then there will be light.
else
digitalWrite (ledpins[x], LOW); // If it is 0, then there will not be light.
// A
//F B
// G
//E C
// D
The error message I get is:
_28.10.2015.ino: In function 'void setup()':
_28.10.2015.ino:7:20: error: 'SetTimer' was not declared in this scope
_28.10.2015.ino:8:14: error: 'StartTimer' was not declared in this scope
_28.10.2015.ino: In function 'void loop()':
_28.10.2015.ino:22:1: error: 'number' was not declared in this scope
_28.10.2015.ino: At global scope:
_28.10.2015.ino:52:1: error: expected '}' before 'if'
_28.10.2015.ino:52:1: error: too many initializers for 'int [7]'
_28.10.2015.ino:52:1: error: expected ',' or ';' before 'if'
Feil ved kompilering.
(Feil ved kompilering=Errors at compile(Norwegian)
The problem is that you are not declaring these functions that you are getting errors, neither the "number" variable.
You need to declare them, like:
int number;
void StartTimer( )
{
// function code;
}
Or include a ".h" that contain these functions, like #Neil Locketz said.
There are quite a few issues with this code.
One of the first things that I notice is that you close out your loop() function with }, then you proceed to write more code that doesn't belong to any function at all.
Also, as #Raul points out, you define an array sevenseg[][], but you do not end the statement with a semicolon.
Your last for() loop is missing its closing brace, }.
Your last for() loop has a semicolon before the opening brace. It shouldn't be there.
You use the variable number in your loop() function, but you define what number is after you use it. You have to define a variable before you use it.
You call SetTimer() and StartTimer() in your setup() function, but those functions are not defined. That's because either 1, you have not included the library where those functions are defined or 2, you did not define those functions yourself. If your issue is 1, then I assume you intended to use #include <SimpleTimer.h>. Note that you also have to install that library. The instructions on how to download it and add it to your Arduino libraries are here. Finally, you have to create a timer object like this: SimpleTimer timer; and then you can call the function like this, timer.SetTimer(your-parameters-here);.
There are probably other things that I have missed, but that should give you a starting point. It looks like you have created a lot of code without testing to see if any of it worked. I would recommend taking this a step at a time... code one logical block and see if it works before you move on to coding your next idea. It may seem like it takes more time but, in the end, it is usually a much faster way to program.
Another suggestion that I would make is to define variables within the function in which you use them. Making all of your variables "global" like you have done is not a good way to write code. For example:
void loop()
{
static int number = 0;
number++;
delay(1000);
if (number > 9)
{
number = 0;
}
}
Note the use of the keyword static. This will ensure that the value stored in number will not go away when the function ends. In other words, the value will still be there the next time the loop() function is called.
Finally, if I had to guess at what you were trying to accomplish, I would think your code should look a little more like this. It appears as though you were trying out different things so I left a number of code snippets in there from your original code that don't actually do anything:
void setup() {
Serial.begin(115200);
for (int i = 0; i < 9; ++i)
{
pinMode (i, OUTPUT);
}
pinMode(9, INPUT);
}
void loop() {
static int counter = 0;
static int last_input_value = LOW;
static bool go_by_switch = true;
if(go_by_switch)
{
int switch_input_value = digitalRead(9);
if(last_input_value == LOW && switch_input_value == HIGH)
{
counter = (counter + 1) % 10;
}
last_input_value = switch_input_value;
}
else
{
delay(500);
counter = (counter + 1) % 10;
}
writeNumber(counter);
}
void writeNumber (int count)
{
#define PIN_COUNT 7
#define NUM_OF_SEGMENTS 7
#define NUM_OF_NUMBERS 10
// 0 6
// pins A B C D E F G
static const int ledpins[PIN_COUNT] = {12, 10, 7, 4, 2, 13, 8};
static const int sevenseg[NUM_OF_NUMBERS][NUM_OF_SEGMENTS] =
{
// A B C D E F G
{1, 1, 1, 1, 1, 1, 0}, //0
{0, 1, 1, 0, 0, 0, 0}, //1
{1, 1, 0, 1, 1, 0, 1}, //2
{1, 1, 1, 1, 0, 0, 1}, //3
{0, 1, 1, 0, 0, 1, 1}, //4
{1, 0, 1, 1, 0, 1, 1}, //5
{1, 0, 1, 1, 1, 1, 1}, //6
{1, 1, 1, 0, 0, 0, 0}, //7
{1, 1, 1, 1, 1, 1, 1}, //8
{1, 1, 1, 1, 0, 1, 1}, //9
};
static int number = 0;
int i;
number++;
delay(1000);
if(number >= NUM_OF_NUMBERS)
{
number = 0;
}
/* Clear all segments of the 7-segment display. */
for (i = 0; i < PIN_COUNT; i++)
{
pinMode (ledpins[i], OUTPUT);
digitalWrite(ledpins[i], LOW);
}
/* Set the 7-segment display with the current number. */
for (i = 0; i < PIN_COUNT; i++)
{
if (sevenseg[number][i]) // sevenseg = 7-segment display
digitalWrite (ledpins[i], HIGH); // If it is 1, then there will be light.
else
digitalWrite (ledpins[i], LOW); // If it is 0, then there will not be light.
}
}